diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index d491434b5f..b7dbc8b598 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -21,7 +21,11 @@ if (WIN32) # FIXME need to account for different architectures set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include CACHE TYPE INTERNAL) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/Win32/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL) + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/x64/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL) + else() + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/Win32/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL) + endif() elseif(APPLE) diff --git a/cmake/externals/glew/CMakeLists.txt b/cmake/externals/glew/CMakeLists.txt index 0d80e7a789..bcf175432c 100644 --- a/cmake/externals/glew/CMakeLists.txt +++ b/cmake/externals/glew/CMakeLists.txt @@ -16,11 +16,16 @@ if (WIN32) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of glew include directories") - - set(_LIB_DIR ${SOURCE_DIR}/lib/Release/Win32) - + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(_LIB_DIR ${SOURCE_DIR}/lib/Release/x64) + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/x64 CACHE FILEPATH "Location of GLEW DLL") + else() + set(_LIB_DIR ${SOURCE_DIR}/lib/Release/Win32) + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/Win32 CACHE FILEPATH "Location of GLEW DLL") + endif() + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/glew32.lib CACHE FILEPATH "Location of GLEW release library") set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of GLEW debug library") - - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/Win32 CACHE FILEPATH "Location of GLEW DLL") + endif () \ No newline at end of file diff --git a/cmake/externals/oglplus/CMakeLists.txt b/cmake/externals/oglplus/CMakeLists.txt index 23ba0d515e..6556f5a61b 100644 --- a/cmake/externals/oglplus/CMakeLists.txt +++ b/cmake/externals/oglplus/CMakeLists.txt @@ -4,8 +4,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - GIT_REPOSITORY https://github.com/jherico/oglplus.git - GIT_TAG 470d8e56fd6bf3913ceec03d82f42d3bafab2cbe + GIT_REPOSITORY https://github.com/matus-chochlik/oglplus.git + GIT_TAG a2681383928b1166f176512cbe0f95e96fe68d08 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/externals/tbb/CMakeLists.txt b/cmake/externals/tbb/CMakeLists.txt index 8f327bd69f..365bd3cf14 100644 --- a/cmake/externals/tbb/CMakeLists.txt +++ b/cmake/externals/tbb/CMakeLists.txt @@ -73,9 +73,14 @@ if (APPLE) ) elseif (WIN32) - set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12") + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/vc12") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/vc12" CACHE PATH "Path to TBB DLLs") + else() + set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs") + endif() set(_LIB_EXT "lib") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs") elseif (ANDROID) set(_TBB_LIB_DIR "${SOURCE_DIR}/lib") set(_LIB_PREFIX "lib") diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake index 0dcbcbb7dd..1f8b7b19fe 100644 --- a/cmake/modules/FindFaceshift.cmake +++ b/cmake/modules/FindFaceshift.cmake @@ -28,7 +28,11 @@ if (APPLE) elseif (UNIX) set(ARCH_DIR "UNIX") elseif (WIN32) - set(ARCH_DIR "Win32") + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(ARCH_DIR "x64") + else() + set(ARCH_DIR "Win32") + endif() endif () find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) diff --git a/cmake/modules/FindNSIGHT.cmake b/cmake/modules/FindNSIGHT.cmake index 4df0686ebe..243c337adb 100644 --- a/cmake/modules/FindNSIGHT.cmake +++ b/cmake/modules/FindNSIGHT.cmake @@ -16,7 +16,16 @@ # if (WIN32) - + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(ARCH_DIR "x64") + set(ARCH_NAME "64") + else() + set(ARCH_DIR "Win32") + set(ARCH_NAME "32") + endif() + + find_path(NSIGHT_INCLUDE_DIRS NAMES nvToolsExt.h @@ -25,17 +34,18 @@ if (WIN32) PATHS "C:/Program Files/NVIDIA Corporation/NvToolsExt") - find_library(NSIGHT_LIBRARY_RELEASE nvToolsExt32_1 + find_library(NSIGHT_LIBRARY_RELEASE "nvToolsExt${ARCH_NAME}_1" PATH_SUFFIXES - "lib/Win32" "lib" + "lib/${ARCH_DIR}" "lib" PATHS "C:/Program Files/NVIDIA Corporation/NvToolsExt") - find_library(NSIGHT_LIBRARY_DEBUG nvToolsExt32_1 + find_library(NSIGHT_LIBRARY_DEBUG "nvToolsExt${ARCH_NAME}_1" PATH_SUFFIXES - "lib/Win32" "lib" + "lib/${ARCH_DIR}" "lib" PATHS "C:/Program Files/NVIDIA Corporation/NvToolsExt") - + + add_paths_to_fixup_libs("C:/Program Files/NVIDIA Corporation/NvToolsExt/bin/${ARCH_DIR}") include(SelectLibraryConfigurations) select_library_configurations(NSIGHT) endif () diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 0f4346c53d..98b37d5410 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -30,12 +30,20 @@ elseif (UNIX) find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) elseif (WIN32) - find_library(SIXENSE_LIBRARY_RELEASE lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/win32/debug_dll/sixensed.lib HINTS ${SIXENSE_SEARCH_DIRS}) + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(ARCH_DIR "x64") + set(ARCH_SUFFIX "_x64") + else() + set(ARCH_DIR "Win32") + set(ARCH_SUFFIX "") + endif() + + find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS}) - find_path(SIXENSE_DEBUG_DLL_PATH sixensed.dll PATH_SUFFIXES bin/win32/debug_dll HINTS ${SIXENSE_SEARCH_DIRS}) - find_path(SIXENSE_RELEASE_DLL_PATH sixense.dll PATH_SUFFIXES bin/win32/release_dll HINTS ${SIXENSE_SEARCH_DIRS}) - find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/win32/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS}) + find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS}) + find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS}) + find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS}) endif () include(SelectLibraryConfigurations) diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake index 4d4b3ca504..1ccdcd792d 100644 --- a/cmake/modules/FindTBB.cmake +++ b/cmake/modules/FindTBB.cmake @@ -49,7 +49,7 @@ elseif (UNIX AND NOT ANDROID) endif () elseif (WIN32) - if (CMAKE_CL_64) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_TBB_ARCH_DIR "intel64") else() set(_TBB_ARCH_DIR "ia32") diff --git a/examples/acScripts/rain.js b/examples/acScripts/rain.js index ee8a1e19a6..3f38b8d36a 100644 --- a/examples/acScripts/rain.js +++ b/examples/acScripts/rain.js @@ -123,7 +123,7 @@ var RainSquall = function (properties) { function setUp() { if (debug) { squallCircle = Overlays.addOverlay("circle3d", { - size: { x: 2 * squallRadius, y: 2 * squallRadius }, + size: { x: squallRadius, y: squallRadius }, color: SQUALL_CIRCLE_COLOR, alpha: SQUALL_CIRCLE_ALPHA, solid: true, diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 7d024e2fd3..a90960a330 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -100,12 +100,12 @@ var NUM_BUTTONS = 3; var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; Script.include(["../../libraries/toolBars.js"]); -const persistKey = "highfidelity.gun.toolbar.position"; -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); -toolBar.save = function () { - Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); -}; -var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) { + return { + x: startX, + y: (screenSize.y - (BUTTON_SIZE + PADDING)), + }; +}); var reticle = Overlays.addOverlay("image", { x: screenSize.x / 2 - (BUTTON_SIZE / 2), y: screenSize.y / 2 - (BUTTON_SIZE / 2), @@ -116,8 +116,6 @@ var reticle = Overlays.addOverlay("image", { }); var offButton = toolBar.addOverlay("image", { - x: old ? old[0] : startX, - y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", @@ -181,7 +179,7 @@ function entityCollisionWithEntity(entity1, entity2, collision) { } function shootBullet(position, velocity, grenade) { - var BULLET_SIZE = 0.10; + var BULLET_SIZE = .09; var BULLET_LIFETIME = 10.0; var BULLET_GRAVITY = -0.25; var GRENADE_VELOCITY = 15.0; diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index d9f19c6a4c..fb76b62dbe 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -18,4 +18,5 @@ Script.load("users.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("mouseLook.js"); +Script.load("hmdControls.js"); Script.load("dialTone.js"); diff --git a/examples/dice.js b/examples/dice.js index ac5d1b7426..ee88984b75 100644 --- a/examples/dice.js +++ b/examples/dice.js @@ -33,15 +33,13 @@ var BUTTON_SIZE = 32; var PADDING = 3; Script.include(["libraries/toolBars.js"]); -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); -const persistKey = "highfidelity.dice.toolbar.position"; -toolBar.save = function () { - Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); -}; -var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.dice.toolbar", function (screenSize) { + return { + x: (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), + y: (screenSize.y - (BUTTON_SIZE + PADDING)) + }; +}); var offButton = toolBar.addOverlay("image", { - x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), - y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", diff --git a/examples/edit.js b/examples/edit.js index 2974397cde..92e4737f29 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -49,7 +49,6 @@ selectionManager.addEventListener(function() { lightOverlayManager.updatePositions(); }); -var windowDimensions = Controller.getViewportDimensions(); var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; var toolHeight = 50; var toolWidth = 50; @@ -143,7 +142,12 @@ var toolBar = (function () { browseMarketplaceButton; function initialize() { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function (windowDimensions, toolbar) { + return { + x: windowDimensions.x - 8 - toolbar.width, + y: (windowDimensions.y - toolbar.height) / 2 + }; + }); browseMarketplaceButton = toolBar.addTool({ imageURL: toolIconUrl + "marketplace.svg", @@ -321,38 +325,6 @@ var toolBar = (function () { } } - const persistKey = "highfidelity.edit.toolbar.position"; - that.move = function () { - var newViewPort, - toolsX, - toolsY; - - newViewPort = Controller.getViewportDimensions(); - - if (toolBar === undefined) { - initialize(); - - toolBar.save = function () { - Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); - }; - var old = JSON.parse(Settings.getValue(persistKey) || '0'); - if (old) { - windowDimensions = newViewPort; - toolBar.move(old[0], old[1]); - return; - } - } else if (windowDimensions.x === newViewPort.x && - windowDimensions.y === newViewPort.y) { - return; - } - - windowDimensions = newViewPort; - toolsX = windowDimensions.x - 8 - toolBar.width; - toolsY = (windowDimensions.y - toolBar.height) / 2; - - toolBar.move(toolsX, toolsY); - }; - var newModelButtonDown = false; var browseMarketplaceButtonDown = false; that.mousePressEvent = function (event) { @@ -552,6 +524,7 @@ var toolBar = (function () { toolBar.cleanup(); }; + initialize(); return that; }()); @@ -931,7 +904,6 @@ var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays Script.update.connect(function (deltaTime) { - toolBar.move(); progressDialog.move(); selectionDisplay.checkMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); diff --git a/examples/hmdControls.js b/examples/hmdControls.js new file mode 100644 index 0000000000..e14ddca3ef --- /dev/null +++ b/examples/hmdControls.js @@ -0,0 +1,285 @@ +// +// hmdControls.js +// examples +// +// Created by Sam Gondelman on 6/17/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 +// + +var MOVE_DISTANCE = 10.0; +var PITCH_INCREMENT = 0.5; // degrees +var pitchChange = 0; // degrees +var YAW_INCREMENT = 0.5; // degrees +var VR_YAW_INCREMENT = 15.0; // degrees +var yawChange = 0; +var BOOM_SPEED = 0.5; +var THRESHOLD = 0.2; + +var CAMERA_UPDATE_TIME = 0.5; +var yawTimer = CAMERA_UPDATE_TIME; + +var shifted = false; +var SHIFT_UPDATE_TIME = 0.5; +var shiftTimer = SHIFT_UPDATE_TIME; +var SHIFT_MAG = 4.0; + +var warpActive = false; +var WARP_UPDATE_TIME = .5; +var warpTimer = WARP_UPDATE_TIME; + +var warpPosition = { x: 0, y: 0, z: 0 }; + +var WARP_SPHERE_SIZE = 1; +var warpSphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: WARP_SPHERE_SIZE, + color: { red: 0, green: 255, blue: 0 }, + alpha: 1.0, + solid: true, + visible: false, +}); + +var WARP_LINE_HEIGHT = 10; +var warpLine = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z:0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 255}, + alpha: 1, + lineWidth: 5, + visible: false, +}); + +var velocity = { x: 0, y: 0, z: 0 }; +var VERY_LONG_TIME = 1000000.0; + +var active = Menu.isOptionChecked("Enable VR Mode"); +var prevVRMode = Menu.isOptionChecked("Enable VR Mode"); + +var hmdControls = (function () { + + function onKeyPressEvent(event) { + if (event.text == 'g' && event.isMeta) { + active = !active; + } + } + + function findAction(name) { + var actions = Controller.getAllActions(); + for (var i = 0; i < actions.length; i++) { + if (actions[i].actionName == name) { + return i; + } + } + // If the action isn't found, it will default to the first available action + return 0; + } + + function onActionEvent(action, state) { + if (!active) { + return; + } + if (state < THRESHOLD) { + if (action == findAction("YAW_LEFT") || action == findAction("YAW_RIGHT")) { + yawTimer = CAMERA_UPDATE_TIME; + } else if (action == findAction("PITCH_UP") || action == findAction("PITCH_DOWN")) { + pitchTimer = CAMERA_UPDATE_TIME; + } + return; + } + switch (action) { + case findAction("LONGITUDINAL_BACKWARD"): + var direction = {x: 0.0, y: 0.0, z:1.0}; + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("LONGITUDINAL_FORWARD"): + var direction = {x: 0.0, y: 0.0, z:-1.0}; + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("LATERAL_LEFT"): + var direction = {x:-1.0, y: 0.0, z: 0.0} + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("LATERAL_RIGHT"): + var direction = {x:1.0, y: 0.0, z: 0.0}; + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("VERTICAL_DOWN"): + var direction = {x: 0.0, y: -1.0, z: 0.0}; + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("VERTICAL_UP"): + var direction = {x: 0.0, y: 1.0, z: 0.0}; + direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE); + velocity = Vec3.sum(velocity, direction); + break; + case findAction("YAW_LEFT"): + if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) { + yawChange = yawChange + (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT); + yawTimer = CAMERA_UPDATE_TIME; + } else if (!Menu.isOptionChecked("Enable VR Mode")) { + yawChange = yawChange + (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT); + } + break; + case findAction("YAW_RIGHT"): + if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) { + yawChange = yawChange - (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT); + yawTimer = CAMERA_UPDATE_TIME; + } else if (!Menu.isOptionChecked("Enable VR Mode")) { + yawChange = yawChange - (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT); + } + break; + case findAction("PITCH_DOWN"): + if (!Menu.isOptionChecked("Enable VR Mode")) { + pitchChange = pitchChange - (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT); + } + break; + case findAction("PITCH_UP"): + if (!Menu.isOptionChecked("Enable VR Mode")) { + pitchChange = pitchChange + (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT); + } + break; + case findAction("SHIFT"): // speed up + if (shiftTimer < 0.0) { + shifted = !shifted; + shiftTimer = SHIFT_UPDATE_TIME; + } + break; + case findAction("ACTION1"): // start/end warp + if (warpTimer < 0.0) { + warpActive = !warpActive; + if (!warpActive) { + finishWarp(); + } + warpTimer = WARP_UPDATE_TIME; + } + break; + case findAction("ACTION2"): // cancel warp + warpActive = false; + Overlays.editOverlay(warpSphere, { + visible: false, + }); + Overlays.editOverlay(warpLine, { + visible: false, + }); + default: + break; + } + } + + function update(dt) { + if (prevVRMode != Menu.isOptionChecked("Enable VR Mode")) { + active = Menu.isOptionChecked("Enable VR Mode"); + prevVRMode = Menu.isOptionChecked("Enable VR Mode"); + } + + if (yawTimer >= 0.0) { + yawTimer = yawTimer - dt; + } + if (shiftTimer >= 0.0) { + shiftTimer = shiftTimer - dt; + } + if (warpTimer >= 0.0) { + warpTimer = warpTimer - dt; + } + + if (warpActive) { + updateWarp(); + } + + if (active) { + Controller.captureActionEvents(); + + MyAvatar.bodyYaw = MyAvatar.bodyYaw + yawChange; + MyAvatar.headPitch = Math.max(-180, Math.min(180, MyAvatar.headPitch + pitchChange)); + yawChange = 0; + pitchChange = 0; + + MyAvatar.motorVelocity = velocity; + MyAvatar.motorTimescale = 0.0; + velocity = { x: 0, y: 0, z: 0 }; + } else { + Controller.releaseActionEvents(); + yawChange = 0; + pitchChange = 0; + MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0} + MyAvatar.motorTimescale = VERY_LONG_TIME; + } + } + + function updateWarp() { + var look = Quat.getFront(Camera.getOrientation()); + var pitch = Math.asin(look.y); + + // Get relative to looking straight down + pitch += Math.PI / 2; + + // Scale up + pitch *= 2; + var distance = pitch * pitch * pitch; + + var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z }); + warpPosition = Vec3.multiply(warpDirection, distance); + warpPosition = Vec3.sum(MyAvatar.position, warpPosition); + + // Commented out until ray picking can be fixed + // var pickRay = { + // origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET), + // direction: { x: 0, y: -1, z: 0 } + // }; + + // var intersection = Entities.findRayIntersection(pickRay); + + // if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) { + // // Warp 1 meter above the object - this is an approximation + // // TODO Get the actual offset to the Avatar's feet and plant them to + // // the object. + // warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 }); + // } + + // Adjust overlays to match warp position + Overlays.editOverlay(warpSphere, { + position: warpPosition, + visible: true, + }); + Overlays.editOverlay(warpLine, { + start: warpPosition, + end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }), + visible: true, + }); + } + + function finishWarp() { + Overlays.editOverlay(warpSphere, { + visible: false, + }); + Overlays.editOverlay(warpLine, { + visible: false, + }); + MyAvatar.position = warpPosition; + } + + function setUp() { + Controller.keyPressEvent.connect(onKeyPressEvent); + + Controller.actionEvent.connect(onActionEvent); + + Script.update.connect(update); + } + + function tearDown() { + Controller.releaseActionEvents(); + MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0} + MyAvatar.motorTimescale = VERY_LONG_TIME; + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); \ No newline at end of file diff --git a/examples/hmdDefaults.js b/examples/hmdDefaults.js index 0096b11777..2b5f333644 100644 --- a/examples/hmdDefaults.js +++ b/examples/hmdDefaults.js @@ -13,4 +13,5 @@ Script.load("progress.js"); Script.load("lobby.js"); Script.load("notifications.js"); Script.load("controllers/oculus/goTo.js"); +Script.load("hmdControls.js"); //Script.load("scripts.js"); // Not created yet diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 27a2929d1c..56eee899f9 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -932,6 +932,7 @@ +
@@ -939,9 +940,12 @@
-
+
+ +
- +
@@ -951,17 +955,6 @@
-
-
Hyperlink
-
Href
-
- -
-
Description
- -
-
-
Locked @@ -976,6 +969,36 @@
+
+
User Data
+
+ +
+
+ + +
+ +
+ +
+
Href
+
+ +
+
+
+
Description
+
+ +
+
+ + +
+ +
+
Position
@@ -1043,6 +1066,11 @@
+ +
+ +
+
Linear Velocity
@@ -1109,6 +1137,21 @@
+
+
Color
+
+
+
R
+
G
+
B
+
+
+ + +
+ +
+
Ignore For Collisions @@ -1144,74 +1187,9 @@
-
-
User Data
-
- -
-
- -
-
Color
-
-
-
R
-
G
-
B
-
-
- -
-
Source URL
-
- -
-
- -
-
Max Particles
-
- -
-
-
-
Particle Life Span
-
- -
-
-
-
Particle Emission Rate
-
- -
-
-
-
Particle Emission Direction
-
-
X
-
Y
-
Z
-
-
-
-
Particle Emission Strength
-
- -
-
-
-
Particle Local Gravity
-
- -
-
-
-
Particle Radius
-
- -
+
+
@@ -1220,6 +1198,7 @@
+
Shape Type
@@ -1280,8 +1259,75 @@
+ +
+ +
+ +
+
Source URL
+
+ +
+
+ + +
+ +
+ +
+
Max Particles
+
+ +
+
+
+
Particle Life Span
+
+ +
+
+
+
Particle Emission Rate
+
+ +
+
+
+
Particle Emission Direction
+
+
X
+
Y
+
Z
+
+
+
+
Particle Emission Strength
+
+ +
+
+
+
Particle Local Gravity
+
+ +
+
+
+
Particle Radius
+
+ +
+
+ + +
+ +
+
-
Text
+
Text Content
@@ -1311,6 +1357,11 @@
+ +
+ +
+
Spot Light @@ -1345,6 +1396,11 @@
+ +
+ +
+
Stage Sun Model Enabled @@ -1431,6 +1487,12 @@
+ + +
+ +
+
Skybox Color
@@ -1446,6 +1508,12 @@
+ + +
+ +
+
Atmosphere Center
@@ -1495,8 +1563,6 @@
-
- diff --git a/examples/html/style.css b/examples/html/style.css index dd2e96ab43..f12cfb6dd0 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -8,7 +8,7 @@ body { background-color: rgb(76, 76, 76); color: rgb(204, 204, 204); font-family: Arial; - font-size: 8.25pt; + font-size: 9.0pt; -webkit-touch-callout: none; -webkit-user-select: none; @@ -44,20 +44,31 @@ body { border: 1.5pt solid black; } -.section-header { +.section-header, .sub-section-header { background: #AAA; border-bottom: 0.75pt solid #CCC; background-color: #333333; color: #999; padding: 3pt; + padding-top: 6pt; } -.section-header label { +.section-header label, .sub-section-header label { font-weight: bold; + font-size: 11pt; +} + +.sub-section-header { + padding-top: 4px; +} + +.sub-section-header label { + font-size: 9pt; } .multi-property-section { } + .property-section { display: block; margin: 10 10; @@ -132,7 +143,7 @@ input.no-spin::-webkit-inner-spin-button { table#entity-table { border-collapse: collapse; font-family: Sans-Serif; - font-size: 7.5pt; + font-size: 9pt; width: 100%; } @@ -156,7 +167,7 @@ table#entity-table { } #entity-table td { - font-size: 8.25pt; + font-size: 9.0pt; border: 0pt black solid; word-wrap: nowrap; white-space: nowrap; @@ -176,29 +187,29 @@ th#entity-type { div.input-area { display: inline-block; - font-size: 7.5pt; + font-size: 9pt; } input { } -#type { - font-size: 10.5pt; +#type, #id { + font-size: 9.0pt; } -#type label { +#type label, #id label { color: rgb(150, 150, 150); } input, textarea { - background-color: rgb(102, 102, 102); - color: rgb(204, 204, 204); + background-color: rgb(63, 63, 63); + color: rgb(255, 255, 255); border: none; - font-size: 7.5pt; + font-size: 9pt; } input:disabled, textarea:disabled { - background-color: rgb(102, 102, 102); + background-color: rgb(63, 63, 63); color: rgb(160, 160, 160); } @@ -224,7 +235,7 @@ input:disabled, textarea:disabled { #properties-list .property { padding: 4pt; border-bottom: 0.75pt solid rgb(63, 63, 63); - min-height: 1em; + min-height: 12pt; } @@ -245,7 +256,6 @@ table#properties-list { font-weight: bold; overflow: hidden; text-overflow: ellipsis; - vertical-align: middle; height: 1.2em; } @@ -270,9 +280,7 @@ div.inner { } td { - - - vertical-align: top; + vertical-align: top; } #no-entities { diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d5e2b24f36..98a71e3b12 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -392,7 +392,8 @@ SelectionDisplay = (function () { var grabberSpotLightCircle = Overlays.addOverlay("circle3d", { color: lightOverlayColor, - isSolid: false + isSolid: false, + visible: false }); var grabberSpotLightLineT = Overlays.addOverlay("line3d", spotLightLineProperties); var grabberSpotLightLineB = Overlays.addOverlay("line3d", spotLightLineProperties); @@ -409,17 +410,20 @@ SelectionDisplay = (function () { var grabberPointLightCircleX = Overlays.addOverlay("circle3d", { rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), color: lightOverlayColor, - isSolid: false + isSolid: false, + visible: false }); var grabberPointLightCircleY = Overlays.addOverlay("circle3d", { rotation: Quat.fromPitchYawRollDegrees(90, 0, 0), color: lightOverlayColor, - isSolid: false + isSolid: false, + visible: false }); var grabberPointLightCircleZ = Overlays.addOverlay("circle3d", { rotation: Quat.fromPitchYawRollDegrees(0, 0, 0), color: lightOverlayColor, - isSolid: false + isSolid: false, + visible: false }); var grabberPointLightT = Overlays.addOverlay("cube", grabberPropertiesEdge); var grabberPointLightB = Overlays.addOverlay("cube", grabberPropertiesEdge); @@ -546,7 +550,7 @@ SelectionDisplay = (function () { var rotateOverlayTarget = Overlays.addOverlay("circle3d", { position: { x:0, y: 0, z: 0}, - size: rotateOverlayTargetSize * 2, + size: rotateOverlayTargetSize, color: { red: 0, green: 0, blue: 0 }, alpha: 0.0, solid: true, @@ -1186,7 +1190,7 @@ SelectionDisplay = (function () { }); Overlays.editOverlay(grabberSpotLightCircle, { position: NEAR, - dimensions: { x: distance * 2, y: distance * 2, z: 1 }, + dimensions: { x: distance, y: distance, z: 1 }, lineWidth: 1.5, rotation: rotation, visible: true, @@ -1258,19 +1262,19 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberPointLightCircleX, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)), - dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleY, { position: position, rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(90, 0, 0)), - dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, visible: true, }); Overlays.editOverlay(grabberPointLightCircleZ, { position: position, rotation: rotation, - dimensions: { x: properties.dimensions.z, y: properties.dimensions.z, z: 1 }, + dimensions: { x: properties.dimensions.z / 2.0, y: properties.dimensions.z / 2.0, z: 1 }, visible: true, }); @@ -1966,7 +1970,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayInner, { visible: true, - size: innerRadius * 2, + size: innerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -1976,7 +1980,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: true, - size: outerRadius * 2, + size: outerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -1986,7 +1990,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: true, - size: outerRadius * 2, + size: outerRadius, startAt: 0, endAt: 0, innerRadius: 0.9, @@ -2064,13 +2068,13 @@ SelectionDisplay = (function () { if (snapToInner) { Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); } else { Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } @@ -2095,7 +2099,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayInner, { visible: true, - size: innerRadius * 2, + size: innerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -2105,7 +2109,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: true, - size: outerRadius * 2, + size: outerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -2115,7 +2119,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: true, - size: outerRadius * 2, + size: outerRadius, startAt: 0, endAt: 0, innerRadius: 0.9, @@ -2186,13 +2190,13 @@ SelectionDisplay = (function () { if (snapToInner) { Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); } else { Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } @@ -2216,7 +2220,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayInner, { visible: true, - size: innerRadius * 2, + size: innerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -2226,7 +2230,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: true, - size: outerRadius * 2, + size: outerRadius, innerRadius: 0.9, startAt: 0, endAt: 360, @@ -2236,7 +2240,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: true, - size: outerRadius * 2, + size: outerRadius, startAt: 0, endAt: 0, innerRadius: 0.9, @@ -2306,13 +2310,13 @@ SelectionDisplay = (function () { if (snapToInner) { Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); } else { Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2, + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); } diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 6e72795ea5..16c68a73fb 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -126,12 +126,12 @@ Tool.prototype = new Overlay2D; Tool.IMAGE_HEIGHT = 50; Tool.IMAGE_WIDTH = 50; -ToolBar = function(x, y, direction) { +ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPositionFunction) { this.tools = new Array(); this.x = x; this.y = y; this.width = 0; - this.height = 0; + this.height = ToolBar.TITLE_BAR_HEIGHT; this.back = this.back = Overlays.addOverlay("text", { backgroundColor: { red: 255, green: 255, blue: 255 }, x: this.x, @@ -336,7 +336,7 @@ ToolBar = function(x, y, direction) { return (that.x <= x) && (x <= (that.x + that.width)) && (that.y <= y) && (y <= (that.y + that.height)); } - that.hover = function (enable) { + that.hover = function (enable) { // Can be overriden or extended by clients. that.isHovering = enable; if (that.back) { if (enable) { @@ -348,16 +348,36 @@ ToolBar = function(x, y, direction) { }); } }; + that.windowDimensions = Controller.getViewportDimensions(); + // Maybe fixme: Keeping the same percent of the window size isn't always the right thing. + // For example, maybe we want "keep the same percentage to whatever two edges are closest to the edge of screen". + // If we change that, the places to do so are onResizeViewport, save (maybe), and the initial move based on Settings, below. + that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients. + var fractionX = that.x / that.windowDimensions.x; + var fractionY = that.y / that.windowDimensions.y; + that.windowDimensions = newSize || Controller.getViewportDimensions(); + that.move(fractionX * that.windowDimensions.x, fractionY * that.windowDimensions.y); + }; + if (optionalPersistenceKey) { + this.fractionKey = optionalPersistenceKey + '.fraction'; + this.save = function () { + var screenSize = Controller.getViewportDimensions(); + var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y}; + Settings.setValue(this.fractionKey, JSON.stringify(fraction)); + } + } else { + this.save = function () { }; // Called on move. Can be overriden or extended by clients. + } // These are currently only doing that which is necessary for toolbar hover and toolbar drag. // They have not yet been extended to tool hover/click/release, etc. this.mousePressEvent = function (event) { - if (!that.contains(event)) { + if (Overlays.getOverlayAtPoint({ x: event.x, y: event.y }) == that.back) { + that.mightBeDragging = true; + that.dragOffsetX = that.x - event.x; + that.dragOffsetY = that.y - event.y; + } else { that.mightBeDragging = false; - return; } - that.mightBeDragging = true; - that.dragOffsetX = that.x - event.x; - that.dragOffsetY = that.y - event.y; }; this.mouseMove = function (event) { if (!that.mightBeDragging || !event.isLeftButton) { @@ -375,15 +395,19 @@ ToolBar = function(x, y, direction) { } that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y); }; + that.checkResize = function () { // Can be overriden or extended, but usually not. See onResizeViewport. + var currentWindowSize = Controller.getViewportDimensions(); + if ((currentWindowSize.x !== that.windowDimensions.x) || (currentWindowSize.y !== that.windowDimensions.y)) { + that.onResizeViewport(currentWindowSize); + } + }; Controller.mousePressEvent.connect(this.mousePressEvent); Controller.mouseMoveEvent.connect(this.mouseMove); - // Called on move. A different approach would be to have all this on the prototype, - // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save. - this.save = function () { }; + Script.update.connect(that.checkResize); // This compatability hack breaks the model, but makes converting existing scripts easier: this.addOverlay = function (ignored, oldSchoolProperties) { var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy - if (that.numberOfTools() === 0) { + if ((that.numberOfTools() === 0) && (properties.x != undefined) && (properties.y != undefined)) { that.move(properties.x, properties.y); } delete properties.x; @@ -392,7 +416,25 @@ ToolBar = function(x, y, direction) { var id = that.tools[index].overlay(); return id; } + if (this.fractionKey || optionalInitialPositionFunction) { + var savedFraction = JSON.parse(Settings.getValue(this.fractionKey) || '0'); // getValue can answer empty string + var screenSize = Controller.getViewportDimensions(); + if (savedFraction) { + // If we have saved data, keep the toolbar at the same proportion of the screen width/height. + that.move(savedFraction.x * screenSize.x, savedFraction.y * screenSize.y); + } else if (!optionalInitialPositionFunction) { + print("No initPosition(screenSize, intializedToolbar) specified for ToolBar"); + } else { + // Call the optionalInitialPositionFunctinon() AFTER the client has had a chance to set up. + var that = this; + Script.setTimeout(function () { + var position = optionalInitialPositionFunction(screenSize, that); + that.move(position.x, position.y); + }, 0); + } + } } ToolBar.SPACING = 4; ToolBar.VERTICAL = 0; ToolBar.HORIZONTAL = 1; +ToolBar.TITLE_BAR_HEIGHT = 10; diff --git a/examples/paint.js b/examples/paint.js index c0cc93afc7..31ffd02506 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -10,17 +10,16 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -Script.include('lineRider.js') -var MAX_POINTS_PER_LINE = 30; -var LINE_LIFETIME = 60 * 5 //5 minute lifetime + +var LINE_DIMENSIONS = 5; +var LIFETIME = 6000; var colorPalette = [{ red: 236, green: 208, blue: 120 }, { - red: 217, + red: 214, green: 91, blue: 67 }, { @@ -40,15 +39,6 @@ var colorPalette = [{ var currentColorIndex = 0; var currentColor = colorPalette[currentColorIndex]; - - -if (hydraCheck() === true) { - HydraPaint(); -} else { - MousePaint(); -} - - function cycleColor() { currentColor = colorPalette[++currentColorIndex]; if (currentColorIndex === colorPalette.length - 1) { @@ -57,42 +47,17 @@ function cycleColor() { } -function hydraCheck() { - var numberOfButtons = Controller.getNumberOfButtons(); - var numberOfTriggers = Controller.getNumberOfTriggers(); - var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); - var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2); - return hydrasConnected; //hydrasConnected; -} - -//************ Mouse Paint ************************** +MousePaint(); function MousePaint() { - var DRAWING_DISTANCE = 2; + var DRAWING_DISTANCE = 5; var lines = []; - var deletedLines = []; var isDrawing = false; - var path = []; - - var lineRider = new LineRider(); - lineRider.addStartHandler(function() { - var points = []; - //create points array from list of all points in path - path.forEach(function(point) { - points.push(point); - }); - lineRider.setPath(points); - }); - - var LINE_WIDTH = 7; - var line; - var points = []; + var line, linePosition; - - var BRUSH_SIZE = 0.02; + var BRUSH_SIZE = .05; var brush = Entities.addEntity({ type: 'Sphere', @@ -110,51 +75,42 @@ function MousePaint() { }); - function newLine(point) { + function newLine(position) { + linePosition = position; line = Entities.addEntity({ - position: MyAvatar.position, + position: position, type: "Line", color: currentColor, dimensions: { - x: 10, - y: 10, - z: 10 + x: LINE_DIMENSIONS, + y: LINE_DIMENSIONS, + z: LINE_DIMENSIONS }, + linePoints: [], lineWidth: LINE_WIDTH, - lifetime: LINE_LIFETIME + lifetime: LIFETIME }); - points = []; - if (point) { - - points.push(point); - path.push(point); - } lines.push(line); } function mouseMoveEvent(event) { + var worldPoint = computeWorldPoint(event); + Entities.editEntity(brush, { + position: worldPoint + }); + if (!isDrawing) { return; } - var pickRay = Camera.computePickRay(event.x, event.y); - var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); - var point = Vec3.sum(Camera.getPosition(), addVector); - points.push(point); - path.push(point); - Entities.editEntity(line, { - linePoints: points - }); - Entities.editEntity(brush, { - position: point - }); + var localPoint = computeLocalPoint(event) + var success = Entities.appendPoint(line, localPoint); - - if (points.length === MAX_POINTS_PER_LINE) { - //We need to start a new line! - newLine(point); + if (!success) { + newLine(worldPoint); + Entities.appendPoint(line, computeLocalPoint(event)); } } @@ -171,17 +127,26 @@ function MousePaint() { lines.push(restoredLine); } + function computeWorldPoint(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); + return Vec3.sum(Camera.getPosition(), addVector); + } + + function computeLocalPoint(event) { + + var localPoint = Vec3.subtract(computeWorldPoint(event), linePosition); + return localPoint; + } + function mousePressEvent(event) { - if(!event.isLeftButton) { + if (!event.isLeftButton) { isDrawing = false; return; } - lineRider.mousePressEvent(event); - path = []; - newLine(); + newLine(computeWorldPoint(event)); isDrawing = true; - } function mouseReleaseEvent() { @@ -198,21 +163,21 @@ function MousePaint() { if (event.text === "z") { undoStroke(); } - if(event.text === "x") { + if (event.text === "x") { redoStroke(); } } + + function cleanup() { lines.forEach(function(line) { - Entities.deleteEntity(line); + // Entities.deleteEntity(line); }); Entities.deleteEntity(brush); - lineRider.cleanup(); } - Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); @@ -222,266 +187,6 @@ function MousePaint() { } - -//*****************HYDRA PAINT ******************************************* - - - -function HydraPaint() { - - - - var lineRider = new LineRider(); - lineRider.addStartHandler(function() { - var points = []; - //create points array from list of all points in path - rightController.path.forEach(function(point) { - points.push(point); - }); - lineRider.setPath(points); - }); - - var LEFT = 0; - var RIGHT = 1; - - var currentTime = 0; - - - var minBrushSize = .02; - var maxBrushSize = .04 - - - var minLineWidth = 5; - var maxLineWidth = 10; - var currentLineWidth = minLineWidth; - var MIN_PAINT_TRIGGER_THRESHOLD = .01; - var COLOR_CHANGE_TIME_FACTOR = 0.1; - - var RIGHT_BUTTON_1 = 7 - var RIGHT_BUTTON_2 = 8 - var RIGHT_BUTTON_3 = 9; - var RIGHT_BUTTON_4 = 10 - - var LEFT_BUTTON_1 = 1; - var LEFT_BUTTON_2 = 2; - var LEFT_BUTTON_3 = 3; - var LEFT_BUTTON_4 = 4; - - var STROKE_SMOOTH_FACTOR = 1; - - var MIN_DRAW_DISTANCE = 0.2; - var MAX_DRAW_DISTANCE = 0.4; - - function controller(side, undoButton, redoButton, cycleColorButton, startRideButton) { - this.triggerHeld = false; - this.triggerThreshold = 0.9; - this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; - this.lines = []; - this.deletedLines = [] //just an array of properties objects - this.isPainting = false; - - this.undoButton = undoButton; - this.undoButtonPressed = false; - this.prevUndoButtonPressed = false; - - this.redoButton = redoButton; - this.redoButtonPressed = false; - this.prevRedoButtonPressed = false; - - this.cycleColorButton = cycleColorButton; - this.cycleColorButtonPressed = false; - this.prevColorCycleButtonPressed = false; - - this.startRideButton = startRideButton; - this.startRideButtonPressed = false; - this.prevStartRideButtonPressed = false; - - this.strokeCount = 0; - this.currentBrushSize = minBrushSize; - this.points = []; - this.path = []; - - this.brush = Entities.addEntity({ - type: 'Sphere', - position: { - x: 0, - y: 0, - z: 0 - }, - color: currentColor, - dimensions: { - x: minBrushSize, - y: minBrushSize, - z: minBrushSize - } - }); - - - this.newLine = function(point) { - this.line = Entities.addEntity({ - position: MyAvatar.position, - type: "Line", - color: currentColor, - dimensions: { - x: 10, - y: 10, - z: 10 - }, - lineWidth: 5, - lifetime: LINE_LIFETIME - }); - this.points = []; - if (point) { - this.points.push(point); - this.path.push(point); - } - this.lines.push(this.line); - } - - this.update = function(deltaTime) { - this.updateControllerState(); - this.avatarPalmOffset = Vec3.subtract(this.palmPosition, MyAvatar.position); - this.projectedForwardDistance = Vec3.dot(Quat.getFront(Camera.getOrientation()), this.avatarPalmOffset); - this.mappedPalmOffset = map(this.projectedForwardDistance, -.5, .5, MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); - this.tipDirection = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)); - this.offsetVector = Vec3.multiply(this.mappedPalmOffset, this.tipDirection); - this.drawPoint = Vec3.sum(this.palmPosition, this.offsetVector); - this.currentBrushSize = map(this.triggerValue, 0, 1, minBrushSize, maxBrushSize); - Entities.editEntity(this.brush, { - position: this.drawPoint, - dimensions: { - x: this.currentBrushSize, - y: this.currentBrushSize, - z: this.currentBrushSize - }, - color: currentColor - }); - if (this.triggerValue > MIN_PAINT_TRIGGER_THRESHOLD) { - if (!this.isPainting) { - this.isPainting = true; - this.newLine(); - this.path = []; - } - if (this.strokeCount % STROKE_SMOOTH_FACTOR === 0) { - this.paint(this.drawPoint); - } - this.strokeCount++; - } else if (this.triggerValue < MIN_PAINT_TRIGGER_THRESHOLD && this.isPainting) { - this.releaseTrigger(); - } - - this.oldPalmPosition = this.palmPosition; - this.oldTipPosition = this.tipPosition; - } - - this.releaseTrigger = function() { - this.isPainting = false; - - } - - - this.updateControllerState = function() { - this.undoButtonPressed = Controller.isButtonPressed(this.undoButton); - this.redoButtonPressed = Controller.isButtonPressed(this.redoButton); - this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); - this.startRideButtonPressed = Controller.isButtonPressed(this.startRideButton); - - //This logic gives us button release - if (this.prevUndoButtonPressed === true && this.undoButtonPressed === false) { - //User released undo button, so undo - this.undoStroke(); - } - if (this.prevRedoButtonPressed === true && this.redoButtonPressed === false) { - this.redoStroke(); - } - - if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { - cycleColor(); - Entities.editEntity(this.brush, { - color: currentColor - }); - } - if (this.prevStartRideButtonPressed === true && this.startRideButtonPressed === false) { - lineRider.toggleRide(); - } - this.prevRedoButtonPressed = this.redoButtonPressed; - this.prevUndoButtonPressed = this.undoButtonPressed; - this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; - this.prevStartRideButtonPressed = this.startRideButtonPressed; - - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); - this.triggerValue = Controller.getTriggerValue(this.trigger); - } - - this.undoStroke = function() { - var deletedLine = this.lines.pop(); - var deletedLineProps = Entities.getEntityProperties(deletedLine); - this.deletedLines.push(deletedLineProps); - Entities.deleteEntity(deletedLine); - } - - this.redoStroke = function() { - var restoredLine = Entities.addEntity(this.deletedLines.pop()); - Entities.addEntity(restoredLine); - this.lines.push(restoredLine); - } - - this.paint = function(point) { - - currentLineWidth = map(this.triggerValue, 0, 1, minLineWidth, maxLineWidth); - this.points.push(point); - this.path.push(point); - Entities.editEntity(this.line, { - linePoints: this.points, - lineWidth: currentLineWidth, - }); - if (this.points.length > MAX_POINTS_PER_LINE) { - this.newLine(point); - } - } - - this.cleanup = function() { - Entities.deleteEntity(this.brush); - this.lines.forEach(function(line) { - Entities.deleteEntity(line); - }); - } - } - - function update(deltaTime) { - rightController.update(deltaTime); - leftController.update(deltaTime); - currentTime += deltaTime; - } - - function cleanup() { - rightController.cleanup(); - leftController.cleanup(); - lineRider.cleanup(); - } - - function mousePressEvent(event) { - lineRider.mousePressEvent(event); - } - - function vectorIsZero(v) { - return v.x === 0 && v.y === 0 && v.z === 0; - } - - - var rightController = new controller(RIGHT, RIGHT_BUTTON_3, RIGHT_BUTTON_4, RIGHT_BUTTON_1, RIGHT_BUTTON_2); - var leftController = new controller(LEFT, LEFT_BUTTON_3, LEFT_BUTTON_4, LEFT_BUTTON_1, LEFT_BUTTON_2); - - Script.update.connect(update); - Script.scriptEnding.connect(cleanup); - Controller.mousePressEvent.connect(mousePressEvent); - -} - function randFloat(low, high) { return low + Math.random() * (high - low); } diff --git a/examples/pointer.js b/examples/pointer.js index ea6b0c233f..83e2cbf776 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -29,19 +29,13 @@ var buttonOnColor = { }; HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var screenSize = Controller.getViewportDimensions(); var userCanPoint = false; Script.include(["libraries/toolBars.js"]); -const persistKey = "highfidelity.pointer.toolbar.position"; -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); -toolBar.save = function () { - Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); -}; -var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.pointer.toolbar", function (screenSize) { + return {x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING)}, +}); var pointerButton = toolBar.addOverlay("image", { - x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png", diff --git a/examples/utilities.js b/examples/utilities.js index 3844e23e14..85e27079a8 100644 --- a/examples/utilities.js +++ b/examples/utilities.js @@ -9,48 +9,52 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -function hslToRgb(hslColor) { - var h = hslColor.hue; - var s = hslColor.sat; - var l = hslColor.light; - var r, g, b; - if (s == 0) { - r = g = b = l; // achromatic - } else { - var hue2rgb = function hue2rgb(p, q, t) { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6) return p + (q - p) * 6 * t; - if (t < 1 / 2) return q; - if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; - return p; + +map = function(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +hslToRgb = function(hslColor) { + var h = hslColor.hue; + var s = hslColor.sat; + var l = hslColor.light; + var r, g, b; + + if (s == 0) { + r = g = b = l; // achromatic + } else { + var hue2rgb = function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); } - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - - return { - red: Math.round(r * 255), - green: Math.round(g * 255), - blue: Math.round(b * 255) - }; + return { + red: Math.round(r * 255), + green: Math.round(g * 255), + blue: Math.round(b * 255) + }; } -function map(value, min1, max1, min2, max2) { - return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); -} -function randFloat(low, high) { - return low + Math.random() * (high - low); + +randFloat = function(low, high) { + return low + Math.random() * (high - low); } -function randInt(low, high) { - return Math.floor(randFloat(low, high)); +randInt = function(low, high) { + return Math.floor(randFloat(low, high)); } \ No newline at end of file diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png new file mode 100644 index 0000000000..408881b585 Binary files /dev/null and b/interface/resources/images/arrow.png differ diff --git a/interface/resources/images/darkgreyarrow.png b/interface/resources/images/darkgreyarrow.png deleted file mode 100644 index 4c9a8a2bbf..0000000000 Binary files a/interface/resources/images/darkgreyarrow.png and /dev/null differ diff --git a/interface/resources/images/left-arrow-disabled.svg b/interface/resources/images/left-arrow-disabled.svg new file mode 100644 index 0000000000..a25123675e --- /dev/null +++ b/interface/resources/images/left-arrow-disabled.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 deleted file mode 100644 index 1e221f3c73..0000000000 Binary files a/interface/resources/images/lightgreyarrow.png and /dev/null differ diff --git a/interface/resources/images/lightgreyarrow.svg b/interface/resources/images/lightgreyarrow.svg deleted file mode 100644 index fbe697c3e9..0000000000 --- a/interface/resources/images/lightgreyarrow.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/interface/resources/images/redarrow.svg b/interface/resources/images/redarrow.svg deleted file mode 100644 index 156f4baa45..0000000000 --- a/interface/resources/images/redarrow.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/interface/resources/images/redarrow_reversed.svg b/interface/resources/images/redarrow_reversed.svg deleted file mode 100644 index 99c72cadb0..0000000000 --- a/interface/resources/images/redarrow_reversed.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/interface/resources/images/right-arrow-disabled.svg b/interface/resources/images/right-arrow-disabled.svg new file mode 100644 index 0000000000..b0c22afe19 --- /dev/null +++ b/interface/resources/images/right-arrow-disabled.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/interface/resources/images/darkgreyarrow.svg b/interface/resources/images/right-arrow.svg similarity index 100% rename from interface/resources/images/darkgreyarrow.svg rename to interface/resources/images/right-arrow.svg diff --git a/interface/resources/images/sepline.png b/interface/resources/images/sepline.png deleted file mode 100644 index 959fcee72f..0000000000 Binary files a/interface/resources/images/sepline.png and /dev/null differ diff --git a/interface/resources/images/sepline.svg b/interface/resources/images/sepline.svg deleted file mode 100644 index 32afaf7148..0000000000 --- a/interface/resources/images/sepline.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -2015-06-12 18:23ZCanvas 1 Navi Bar diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 7531ee3d69..3852ea3819 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -46,10 +46,40 @@ DialogContainer { property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area property int inputAreaStep: (height - inputAreaHeight) / 2 + MouseArea { + // Drag the icon + width: parent.height + height: parent.height + x: 0 + y: 0 + drag { + target: root + minimumX: -parent.inputAreaStep + minimumY: -parent.inputAreaStep + maximumX: root.parent ? root.maximumX : 0 + maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 + } + } + + MouseArea { + // Drag the input rectangle + width: parent.width - parent.height + height: parent.inputAreaHeight + x: parent.height + y: parent.inputAreaStep + drag { + target: root + minimumX: -parent.inputAreaStep + minimumY: -parent.inputAreaStep + maximumX: root.parent ? root.maximumX : 0 + maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 + } + } + Image { id: backArrow - source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/redarrow_reversed.svg" + source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/left-arrow-disabled.svg" anchors { fill: parent @@ -71,7 +101,7 @@ DialogContainer { Image { id: forwardArrow - source: addressBarDialog.forwardEnabled ? "../images/darkgreyarrow.svg" : "../images/redarrow.svg" + source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/right-arrow-disabled.svg" anchors { fill: parent @@ -111,38 +141,7 @@ DialogContainer { addressBarDialog.loadAddress(addressLine.text) } } - - MouseArea { - // Drag the icon - width: parent.height - height: parent.height - x: 0 - y: 0 - drag { - target: root - minimumX: -parent.inputAreaStep - minimumY: -parent.inputAreaStep - maximumX: root.parent ? root.maximumX : 0 - maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 - } - } - // Add this code to make text bar draggable - /* - MouseArea { - // Drag the input rectangle - width: parent.width - parent.height - height: parent.inputAreaHeight - x: parent.height - y: parent.inputAreaStep - drag { - target: root - minimumX: -parent.inputAreaStep - minimumY: -parent.inputAreaStep - maximumX: root.parent ? root.maximumX : 0 - maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 - } - }*/ } } diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml new file mode 100644 index 0000000000..7888ffd967 --- /dev/null +++ b/interface/resources/qml/AvatarInputs.qml @@ -0,0 +1,177 @@ +// +// Created by Bradley Austin Davis on 2015/06/19 +// 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 +// + +import Hifi 1.0 as Hifi +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtGraphicalEffects 1.0 + +Hifi.AvatarInputs { + id: root + objectName: "AvatarInputs" + anchors.fill: parent + +// width: 800 +// height: 600 +// color: "black" + readonly property int iconPadding: 5 + readonly property int mirrorHeight: 215 + readonly property int mirrorWidth: 265 + readonly property int mirrorTopPad: iconPadding + readonly property int mirrorLeftPad: 10 + readonly property int iconSize: 24 + + Item { + id: mirror + width: root.mirrorWidth + height: root.mirrorVisible ? root.mirrorHeight : 0 + visible: root.mirrorVisible + anchors.left: parent.left + anchors.leftMargin: root.mirrorLeftPad + anchors.top: parent.top + anchors.topMargin: root.mirrorTopPad + clip: true + + MouseArea { + id: hover + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + } + + Image { + id: closeMirror + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.top: parent.top + anchors.topMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: "../images/close.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.closeMirror(); + } + } + } + + Image { + id: zoomIn + visible: hover.containsMouse + width: root.iconSize + height: root.iconSize + anchors.bottom: parent.bottom + anchors.bottomMargin: root.iconPadding + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleZoom(); + } + } + } + } + + Item { + width: root.mirrorWidth + height: 44 + + x: root.mirrorLeftPad + y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5 + + + + Rectangle { + anchors.fill: parent + color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000" + + Image { + id: faceMute + width: root.iconSize + height: root.iconSize + visible: root.cameraEnabled + anchors.left: parent.left + anchors.leftMargin: root.iconPadding + anchors.verticalCenter: parent.verticalCenter + source: root.cameraMuted ? "../images/face-mute.svg" : "../images/face.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleCameraMute() + } + onDoubleClicked: { + root.resetSensors(); + } + } + } + + Image { + id: micMute + width: root.iconSize + height: root.iconSize + anchors.left: root.cameraEnabled ? faceMute.right : parent.left + anchors.leftMargin: root.iconPadding + anchors.verticalCenter: parent.verticalCenter + source: root.audioMuted ? "../images/mic-mute.svg" : "../images/mic.svg" + MouseArea { + anchors.fill: parent + onClicked: { + root.toggleAudioMute() + } + } + } + + Item { + id: audioMeter + anchors.verticalCenter: parent.verticalCenter + anchors.left: micMute.right + anchors.leftMargin: root.iconPadding + anchors.right: parent.right + anchors.rightMargin: root.iconPadding + height: 8 + Rectangle { + id: blueRect + color: "blue" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: parent.width / 4 + } + Rectangle { + id: greenRect + color: "green" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: blueRect.right + anchors.right: redRect.left + } + Rectangle { + id: redRect + color: "red" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: parent.width / 5 + } + Rectangle { + z: 100 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: (1.0 - root.audioLevel) * parent.width + color: "black" + } + } + } + } +} + diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml new file mode 100644 index 0000000000..996f9f02ef --- /dev/null +++ b/interface/resources/qml/Stats.qml @@ -0,0 +1,249 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Item { + anchors.fill: parent + anchors.leftMargin: 300 + Hifi.Stats { + id: root + objectName: "Stats" + implicitHeight: row.height + implicitWidth: row.width + + anchors.horizontalCenter: parent.horizontalCenter + readonly property int fontSize: 12 + readonly property string fontColor: "white" + readonly property string bgColor: "#99333333" + + Row { + id: row + spacing: 8 + Rectangle { + width: generalCol.width + 8; + height: generalCol.height + 8; + color: root.bgColor; + + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + + Column { + id: generalCol + spacing: 4; x: 4; y: 4; + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Servers: " + root.serverCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Avatars: " + root.avatarCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Framerate: " + root.framerate + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) + } + } + } + + Rectangle { + width: pingCol.width + 8 + height: pingCol.height + 8 + color: root.bgColor; + visible: root.audioPing != -2 + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: pingCol + spacing: 4; x: 4; y: 4; + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Audio ping: " + root.audioPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Avatar ping: " + root.avatarPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + text: "Entities avg ping: " + root.entitiesPing + } + Text { + color: root.fontColor + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Voxel max ping: " + 0 + } + } + } + + Rectangle { + width: geoCol.width + 8 + height: geoCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: geoCol + spacing: 4; x: 4; y: 4; + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Position: " + root.position.x.toFixed(1) + ", " + + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Velocity: " + root.velocity.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Yaw: " + root.yaw.toFixed(1) + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " + + root.avatarMixerPps + "pps"; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "Downloads: "; + } + } + } + Rectangle { + width: octreeCol.width + 8 + height: octreeCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + } + Column { + id: octreeCol + spacing: 4; x: 4; y: 4; + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + text: "Triangles: " + root.triangles + + " / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + + " / Translucent: " + root.meshTranslucent; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tOpaque considered: " + root.opaqueConsidered + + " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: !root.expanded + text: "Octree Elements Server: " + root.serverElements + + " Local: " + root.localElements; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Sending Mode: " + root.sendingMode; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Packets to Process: " + root.packetStats; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Octree Elements - "; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tServer: " + root.serverElements + + " Internal: " + root.serverInternal + + " Leaves: " + root.serverLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "\tLocal: " + root.localElements + + " Internal: " + root.localInternal + + " Leaves: " + root.localLeaves; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "LOD: " + root.lodStatus; + } + } + } + } + + Rectangle { + y: 250 + visible: root.timingExpanded + width: perfText.width + 8 + height: perfText.height + 8 + color: root.bgColor; + Text { + x: 4; y: 4 + id: perfText + color: root.fontColor + font.family: root.monospaceFont + font.pixelSize: 12 + text: "------------------------------------------ Function " + + "--------------------------------------- --msecs- -calls--\n" + + root.timingStats; + } + } + + Connections { + target: root.parent + onWidthChanged: { + root.x = root.parent.width - root.width; + } + } + } + +} diff --git a/interface/resources/qml/Tooltip.qml b/interface/resources/qml/Tooltip.qml new file mode 100644 index 0000000000..c836030ba1 --- /dev/null +++ b/interface/resources/qml/Tooltip.qml @@ -0,0 +1,31 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 as Original +import "controls" +import "styles" + +Hifi.Tooltip { + id: root + HifiConstants { id: hifi } + // FIXME adjust position based on the edges of the screen + x: (lastMousePosition.x > surfaceSize.width/2) ? lastMousePosition.x - 140 : lastMousePosition.x + 20 + //y: lastMousePosition.y + 5 + y: (lastMousePosition.y > surfaceSize.height/2) ? lastMousePosition.y - 70 : lastMousePosition.y + 5 + implicitWidth: border.implicitWidth + implicitHeight: border.implicitHeight + + Border { + id: border + anchors.fill: parent + implicitWidth: text.implicitWidth + implicitHeight: Math.max(text.implicitHeight, 64) + + Text { + id: text + anchors.fill: parent + anchors.margins: 16 + font.pixelSize: hifi.fonts.pixelSize / 2 + text: root.text + wrapMode: Original.Text.WordWrap + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 06b086ccb3..f828f05258 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -66,6 +66,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -83,12 +86,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -106,11 +111,9 @@ #include "avatar/AvatarManager.h" -#include "audio/AudioToolBox.h" #include "audio/AudioIOStatsRenderer.h" #include "audio/AudioScope.h" -#include "devices/CameraToolBox.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" #include "devices/Leapmotion.h" @@ -120,12 +123,6 @@ #include "devices/OculusManager.h" #include "devices/TV3DManager.h" -#include "gpu/Batch.h" -#include "gpu/Context.h" -#include "gpu/GLBackend.h" - -#include "RenderDeferredTask.h" - #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" @@ -141,6 +138,7 @@ #include "SpeechRecognizer.h" #endif +#include "ui/AvatarInputs.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" @@ -281,8 +279,6 @@ bool setupEssentials(int& argc, char** argv) { auto animationCache = DependencyManager::set(); auto ddeFaceTracker = DependencyManager::set(); auto modelBlender = DependencyManager::set(); - auto audioToolBox = DependencyManager::set(); - auto cameraToolBox = DependencyManager::set(); auto avatarManager = DependencyManager::set(); auto lodManager = DependencyManager::set(); auto jsConsole = DependencyManager::set(); @@ -335,8 +331,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _cursorVisible(true), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), - _touchAvgX(0.0f), - _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), _enableProcessOctreeThread(true), @@ -831,6 +825,7 @@ void Application::initializeUi() { LoginDialog::registerType(); MessageDialog::registerType(); VrMenu::registerType(); + Tooltip::registerType(); UpdateDialog::registerType(); auto offscreenUi = DependencyManager::get(); @@ -844,7 +839,7 @@ void Application::initializeUi() { VrMenu::executeQueuedLambdas(); offscreenUi->setMouseTranslator([this](const QPointF& p){ if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); + glm::vec2 pos = _compositor.screenToOverlay(toGlm(p)); return QPointF(pos.x, pos.y); } return QPointF(p); @@ -877,6 +872,7 @@ void Application::paintGL() { OculusManager::beginFrameTiming(); } + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); @@ -884,6 +880,12 @@ void Application::paintGL() { { PerformanceTimer perfTimer("renderOverlay"); + /* + gpu::Context context(new gpu::GLBackend()); + RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + */ _applicationOverlay.renderOverlay(&renderArgs); } @@ -969,9 +971,7 @@ void Application::paintGL() { glPopMatrix(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(&renderArgs, _mirrorViewRect); } @@ -979,16 +979,18 @@ void Application::paintGL() { auto finalFbo = DependencyManager::get()->render(&renderArgs); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y, 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), - GL_COLOR_BUFFER_BIT, GL_NEAREST); + GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - _applicationOverlay.displayOverlayTexture(); + _compositor.displayOverlayTexture(&renderArgs); } + if (!OculusManager::isConnected() || OculusManager::allowSwap()) { _glWidget->swapBuffers(); } @@ -1046,27 +1048,24 @@ void Application::resizeGL() { } else { renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); } - if (_renderResolution == toGlm(renderSize)) { - return; + + if (_renderResolution != toGlm(renderSize)) { + _renderResolution = toGlm(renderSize); + DependencyManager::get()->setFrameBufferSize(renderSize); + resetCamerasOnResizeGL(_myCamera, _renderResolution); + + glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? + + updateProjectionMatrix(); + glLoadIdentity(); } - _renderResolution = toGlm(renderSize); - DependencyManager::get()->setFrameBufferSize(renderSize); - resetCamerasOnResizeGL(_myCamera, _renderResolution); - - glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? - - updateProjectionMatrix(); - glLoadIdentity(); - auto offscreenUi = DependencyManager::get(); - offscreenUi->resize(_glWidget->size()); + + auto canvasSize = _glWidget->size(); + offscreenUi->resize(canvasSize); _glWidget->makeCurrent(); - // update Stats width - // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; - Stats::getInstance()->resetWidth(_renderResolution.x, horizontalOffset); } void Application::updateProjectionMatrix() { @@ -1564,27 +1563,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { _keyboardMouseDevice.mousePressEvent(event); if (event->button() == Qt::LeftButton) { - _mouseDragStartedX = getTrueMouseX(); - _mouseDragStartedY = getTrueMouseY(); + _mouseDragStarted = getTrueMouse(); _mousePressed = true; - if (mouseOnScreen()) { - if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - - if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - - if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - } - // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), computePickRay(event->x(), event->y())); @@ -1603,17 +1584,6 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI } _controllerScriptingInterface.emitMouseDoublePressEvent(event); - - if (activeWindow() == _window) { - if (event->button() == Qt::LeftButton) { - if (mouseOnScreen()) { - if (DependencyManager::get()->mouseDoublePressEvent(getMouseX(), getMouseY())) { - // stop propagation - return; - } - } - } - } } void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1635,13 +1605,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { if (event->button() == Qt::LeftButton) { _mousePressed = false; - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { - // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH; - Stats::getInstance()->checkClick(getMouseX(), getMouseY(), - getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); - } - // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), computePickRay(event->x(), event->y())); @@ -1669,22 +1632,18 @@ void Application::touchUpdateEvent(QTouchEvent* event) { bool validTouch = false; if (activeWindow() == _window) { const QList& tPoints = event->touchPoints(); - _touchAvgX = 0.0f; - _touchAvgY = 0.0f; + _touchAvg = vec2(); int numTouches = tPoints.count(); if (numTouches > 1) { for (int i = 0; i < numTouches; ++i) { - _touchAvgX += (float)tPoints[i].pos().x(); - _touchAvgY += (float)tPoints[i].pos().y(); + _touchAvg += toGlm(tPoints[i].pos()); } - _touchAvgX /= (float)(numTouches); - _touchAvgY /= (float)(numTouches); + _touchAvg /= (float)(numTouches); validTouch = true; } } if (!_isTouchPressed) { - _touchDragStartedAvgX = _touchAvgX; - _touchDragStartedAvgY = _touchAvgY; + _touchDragStartedAvg = _touchAvg; } _isTouchPressed = validTouch; } @@ -1720,8 +1679,7 @@ void Application::touchEndEvent(QTouchEvent* event) { _keyboardMouseDevice.touchEndEvent(event); // put any application specific touch behavior below here.. - _touchDragStartedAvgX = _touchAvgX; - _touchDragStartedAvgY = _touchAvgY; + _touchDragStartedAvg = _touchAvg; _isTouchPressed = false; } @@ -1870,9 +1828,9 @@ void Application::setFullscreen(bool fullscreen) { Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); } -// The following code block is useful on platforms that can have a visible -// app menu in a fullscreen window. However the OSX mechanism hides the -// application menu for fullscreen apps, so the check is not required. + // The following code block is useful on platforms that can have a visible + // app menu in a fullscreen window. However the OSX mechanism hides the + // application menu for fullscreen apps, so the check is not required. #ifndef Q_OS_MAC if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { if (fullscreen) { @@ -1946,11 +1904,6 @@ void Application::setEnableVRMode(bool enableVRMode) { } else { OculusManager::abandonCalibration(); OculusManager::disconnect(); - - _mirrorCamera.setHmdPosition(glm::vec3()); - _mirrorCamera.setHmdRotation(glm::quat()); - _myCamera.setHmdPosition(glm::vec3()); - _myCamera.setHmdRotation(glm::quat()); } resizeGL(); @@ -1962,44 +1915,34 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { bool Application::mouseOnScreen() const { if (OculusManager::isConnected()) { - return getMouseX() >= 0 && getMouseX() <= _glWidget->getDeviceWidth() && - getMouseY() >= 0 && getMouseY() <= _glWidget->getDeviceHeight(); + ivec2 mouse = getMouse(); + if (!glm::all(glm::greaterThanEqual(mouse, ivec2()))) { + return false; + } + ivec2 size = toGlm(_glWidget->getDeviceSize()); + if (!glm::all(glm::lessThanEqual(mouse, size))) { + return false; + } } return true; } -int Application::getMouseX() const { +ivec2 Application::getMouse() const { if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); - return pos.x; + return _compositor.screenToOverlay(getTrueMouse()); } - return getTrueMouseX(); + return getTrueMouse(); } -int Application::getMouseY() const { +ivec2 Application::getMouseDragStarted() const { if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); - return pos.y; + return _compositor.screenToOverlay(getTrueMouseDragStarted()); } - return getTrueMouseY(); + return getTrueMouseDragStarted(); } -int Application::getMouseDragStartedX() const { - if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), - getTrueMouseDragStartedY())); - return pos.x; - } - return getTrueMouseDragStartedX(); -} - -int Application::getMouseDragStartedY() const { - if (OculusManager::isConnected()) { - glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), - getTrueMouseDragStartedY())); - return pos.y; - } - return getTrueMouseDragStartedY(); +ivec2 Application::getTrueMouseDragStarted() const { + return _mouseDragStarted; } FaceTracker* Application::getActiveFaceTracker() { @@ -2244,13 +2187,6 @@ void Application::init() { _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); - _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); - - connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); - connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); - connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); - connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); - // initialize the GlowEffect with our widget bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); DependencyManager::get()->init(glow); @@ -2515,16 +2451,18 @@ void Application::update(float deltaTime) { // Transfer the user inputs to the driveKeys _myAvatar->clearDriveKeys(); - _myAvatar->setDriveKeys(FWD, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); - _myAvatar->setDriveKeys(BACK, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); - _myAvatar->setDriveKeys(UP, _userInputMapper.getActionState(UserInputMapper::VERTICAL_UP)); - _myAvatar->setDriveKeys(DOWN, _userInputMapper.getActionState(UserInputMapper::VERTICAL_DOWN)); - _myAvatar->setDriveKeys(LEFT, _userInputMapper.getActionState(UserInputMapper::LATERAL_LEFT)); - _myAvatar->setDriveKeys(RIGHT, _userInputMapper.getActionState(UserInputMapper::LATERAL_RIGHT)); - _myAvatar->setDriveKeys(ROT_UP, _userInputMapper.getActionState(UserInputMapper::PITCH_UP)); - _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)); + if (!_controllerScriptingInterface.areActionsCaptured()) { + _myAvatar->setDriveKeys(FWD, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); + _myAvatar->setDriveKeys(BACK, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); + _myAvatar->setDriveKeys(UP, _userInputMapper.getActionState(UserInputMapper::VERTICAL_UP)); + _myAvatar->setDriveKeys(DOWN, _userInputMapper.getActionState(UserInputMapper::VERTICAL_DOWN)); + _myAvatar->setDriveKeys(LEFT, _userInputMapper.getActionState(UserInputMapper::LATERAL_LEFT)); + _myAvatar->setDriveKeys(RIGHT, _userInputMapper.getActionState(UserInputMapper::LATERAL_RIGHT)); + _myAvatar->setDriveKeys(ROT_UP, _userInputMapper.getActionState(UserInputMapper::PITCH_UP)); + _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)); @@ -3150,7 +3088,7 @@ PickRay Application::computePickRay(float x, float y) const { y /= size.y; PickRay result; if (isHMDMode()) { - getApplicationOverlay().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); + getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); } else { if (QThread::currentThread() == activeRenderingThread) { getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction); @@ -3179,13 +3117,11 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, 0); - return image; } @@ -3370,6 +3306,11 @@ namespace render { void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly, bool billboard) { + + // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering. + // Then we can move this logic into the Avatar::simulate call. + _myAvatar->preRender(renderArgs); + activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("display"); @@ -3381,9 +3322,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // flip x if in mirror mode (also requires reversing winding order for backface culling) if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - glScalef(-1.0f, 1.0f, 1.0f); - glFrontFace(GL_CW); - + //glScalef(-1.0f, 1.0f, 1.0f); + //glFrontFace(GL_CW); } else { glFrontFace(GL_CCW); } @@ -3406,7 +3346,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se viewTransform.setTranslation(theCamera.getPosition()); viewTransform.setRotation(rotation); if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); +// viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } if (renderArgs->_renderSide != RenderArgs::MONO) { glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); @@ -3689,7 +3629,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi _mirrorCamera.setPosition(_myAvatar->getPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); - } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { + } else if (!AvatarInputs::getInstance()->mirrorZoomed()) { _mirrorCamera.setPosition(_myAvatar->getChestPosition() + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); @@ -3739,10 +3679,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi displaySide(renderArgs, _mirrorCamera, true, billboard); glPopMatrix(); - if (!billboard) { - _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); - } - // reset Viewport and projection matrix glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glDisable(GL_SCISSOR_TEST); @@ -4749,11 +4685,9 @@ void Application::postLambdaEvent(std::function f) { } void Application::initPlugins() { - OculusManager::init(); } void Application::shutdownPlugins() { - OculusManager::deinit(); } glm::vec3 Application::getHeadPosition() const { @@ -4773,12 +4707,8 @@ QSize Application::getDeviceSize() const { return _glWidget->getDeviceSize(); } -int Application::getTrueMouseX() const { - return _glWidget->mapFromGlobal(QCursor::pos()).x(); -} - -int Application::getTrueMouseY() const { - return _glWidget->mapFromGlobal(QCursor::pos()).y(); +ivec2 Application::getTrueMouse() const { + return toGlm(_glWidget->mapFromGlobal(QCursor::pos())); } bool Application::isThrottleRendering() const { @@ -4808,3 +4738,25 @@ qreal Application::getDevicePixelRatio() { return _window ? _window->windowHandle()->devicePixelRatio() : 1.0; } +mat4 Application::getEyeProjection(int eye) const { + if (isHMDMode()) { + return OculusManager::getEyeProjection(eye); + } + + return _viewFrustum.getProjection(); +} + +mat4 Application::getEyePose(int eye) const { + if (isHMDMode()) { + return OculusManager::getEyePose(eye); + } + + return mat4(); +} + +mat4 Application::getHeadPose() const { + if (isHMDMode()) { + return OculusManager::getHeadPose(); + } + return mat4(); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 3239b84c6c..b126757621 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -62,12 +62,12 @@ #include "ui/ModelsBrowser.h" #include "ui/NodeBounds.h" #include "ui/OctreeStatsDialog.h" -#include "ui/RearMirrorTools.h" #include "ui/SnapshotShareDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" #include "ui/overlays/Overlays.h" #include "ui/ApplicationOverlay.h" +#include "ui/ApplicationCompositor.h" #include "ui/RunningScriptsWidget.h" #include "ui/ToolWindow.h" #include "ui/UserInputMapper.h" @@ -221,15 +221,21 @@ public: const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } bool mouseOnScreen() const; - int getMouseX() const; - int getMouseY() const; - glm::ivec2 getTrueMousePosition() const; - int getTrueMouseX() const; - int getTrueMouseY() const; - int getMouseDragStartedX() const; - int getMouseDragStartedY() const; - int getTrueMouseDragStartedX() const { return _mouseDragStartedX; } - int getTrueMouseDragStartedY() const { return _mouseDragStartedY; } + + ivec2 getMouse() const; + ivec2 getTrueMouse() const; + ivec2 getMouseDragStarted() const; + ivec2 getTrueMouseDragStarted() const; + + // TODO get rid of these and use glm types directly + int getMouseX() const { return getMouse().x; } + int getMouseY() const { return getMouse().y; } + int getTrueMouseX() const { return getTrueMouse().x; } + int getTrueMouseY() const { return getTrueMouse().y; } + int getMouseDragStartedX() const { return getMouseDragStarted().x; } + int getMouseDragStartedY() const { return getMouseDragStarted().y; } + int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; } + int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; } bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; } FaceTracker* getActiveFaceTracker(); @@ -238,6 +244,8 @@ public: QSystemTrayIcon* getTrayIcon() { return _trayIcon; } ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } + ApplicationCompositor& getApplicationCompositor() { return _compositor; } + const ApplicationCompositor& getApplicationCompositor() const { return _compositor; } Overlays& getOverlays() { return _overlays; } float getFps() const { return _fps; } @@ -330,6 +338,9 @@ public: bool isHMDMode() const; glm::quat getHeadOrientation() const; glm::vec3 getHeadPosition() const; + glm::mat4 getHeadPose() const; + glm::mat4 getEyePose(int eye) const; + glm::mat4 getEyeProjection(int eye) const; QRect getDesirableApplicationGeometry(); RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } @@ -548,21 +559,17 @@ private: OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers - KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad - - UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer - - MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) - - Camera _myCamera; // My view onto the world + KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad + UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer + MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) + Camera _myCamera; // My view onto the world Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; - RearMirrorTools* _rearMirrorTools; - Setting::Handle _firstRun; - Setting::Handle _previousScriptLocation; - Setting::Handle _scriptsLocationHandle; - Setting::Handle _fieldOfView; + Setting::Handle _firstRun; + Setting::Handle _previousScriptLocation; + Setting::Handle _scriptsLocationHandle; + Setting::Handle _fieldOfView; Transform _viewTransform; glm::mat4 _untranslatedViewMatrix; @@ -580,18 +587,17 @@ private: Environment _environment; bool _cursorVisible; - int _mouseDragStartedX; - int _mouseDragStartedY; + ivec2 _mouseDragStarted; + quint64 _lastMouseMove; bool _lastMouseMoveWasSimulated; glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayDirection; - float _touchAvgX; - float _touchAvgY; - float _touchDragStartedAvgX; - float _touchDragStartedAvgY; + vec2 _touchAvg; + vec2 _touchDragStartedAvg; + bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) bool _mousePressed; // true if mouse has been pressed (clear when finished) @@ -669,6 +675,7 @@ private: Overlays _overlays; ApplicationOverlay _applicationOverlay; + ApplicationCompositor _compositor; }; #endif // hifi_Application_h diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e501b91dea..42ac30dbf3 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -49,8 +49,6 @@ Camera::Camera() : _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)), - _hmdPosition(), - _hmdRotation(), _isKeepLookingAt(false), _lookingAt(0.0f, 0.0f, 0.0f) { @@ -74,20 +72,6 @@ void Camera::setRotation(const glm::quat& rotation) { } } -void Camera::setHmdPosition(const glm::vec3& hmdPosition) { - _hmdPosition = hmdPosition; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } -} - -void Camera::setHmdRotation(const glm::quat& hmdRotation) { - _hmdRotation = hmdRotation; - if (_isKeepLookingAt) { - lookAt(_lookingAt); - } -} - void Camera::setMode(CameraMode mode) { _mode = mode; emit modeUpdated(modeToString(mode)); diff --git a/interface/src/Camera.h b/interface/src/Camera.h index fc1dbcaf00..6eed39cf16 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -45,26 +45,22 @@ public: void setRotation(const glm::quat& rotation); void setProjection(const glm::mat4 & projection); - void setHmdPosition(const glm::vec3& hmdPosition); - void setHmdRotation(const glm::quat& hmdRotation); void setMode(CameraMode m); - glm::quat getRotation() const { return _rotation * _hmdRotation; } + glm::quat getRotation() const { return _rotation; } const glm::mat4& getProjection() const { return _projection; } - const glm::vec3& getHmdPosition() const { return _hmdPosition; } - const glm::quat& getHmdRotation() const { return _hmdRotation; } CameraMode getMode() const { return _mode; } public slots: QString getModeString() const; void setModeString(const QString& mode); - glm::vec3 getPosition() const { return _position + _hmdPosition; } + glm::vec3 getPosition() const { return _position; } void setPosition(const glm::vec3& position); - void setOrientation(const glm::quat& orientation) { setRotation(orientation); } glm::quat getOrientation() const { return getRotation(); } - + void setOrientation(const glm::quat& orientation) { setRotation(orientation); } + PickRay computePickRay(float x, float y); // These only work on independent cameras @@ -86,8 +82,6 @@ private: glm::vec3 _position; glm::quat _rotation; glm::mat4 _projection; - glm::vec3 _hmdPosition; - glm::quat _hmdRotation; bool _isKeepLookingAt; glm::vec3 _lookingAt; }; diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp deleted file mode 100644 index 68328e151e..0000000000 --- a/interface/src/audio/AudioToolBox.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// AudioToolBox.cpp -// interface/src/audio -// -// Created by Stephen Birarda on 2014-12-16. -// 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 "InterfaceConfig.h" - -#include -#include -#include -#include -#include - -#include "Application.h" -#include "AudioToolBox.h" - -// Mute icon configration -const int MUTE_ICON_SIZE = 24; - -AudioToolBox::AudioToolBox() : - _iconPulseTimeReference(usecTimestampNow()) -{ -} - -bool AudioToolBox::mousePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - DependencyManager::get()->toggleMute(); - return true; - } - return false; -} - -void AudioToolBox::render(int x, int y, int padding, bool boxed) { - glEnable(GL_TEXTURE_2D); - - if (!_micTexture) { - _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); - } - if (!_muteTexture) { - _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); - } - if (_boxTexture) { - _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); - } - - auto audioIO = DependencyManager::get(); - - if (boxed) { - bool isClipping = ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)); - const int BOX_LEFT_PADDING = 5; - const int BOX_TOP_PADDING = 10; - const int BOX_WIDTH = 266; - const int BOX_HEIGHT = 44; - - QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT); - glm::vec4 quadColor; - - if (isClipping) { - quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); - } else { - quadColor = glm::vec4(0.41f, 0.41f, 0.41f, 1.0f); - } - glm::vec2 topLeft(boxBounds.left(), boxBounds.top()); - glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom()); - static const glm::vec2 texCoordTopLeft(1,1); - static const glm::vec2 texCoordBottomRight(0, 0); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture)); - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); - } - - float iconColor = 1.0f; - - _iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - if (!audioIO->isMuted()) { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture)); - iconColor = 1.0f; - } else { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture)); - - // Make muted icon pulsate - static const float PULSE_MIN = 0.4f; - static const float PULSE_MAX = 1.0f; - static const float PULSE_FREQUENCY = 1.0f; // in Hz - qint64 now = usecTimestampNow(); - if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { - // Prevents t from getting too big, which would diminish glm::cos precision - _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); - } - float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; - float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; - iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; - } - - glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); - - glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); - glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); - glm::vec2 texCoordTopLeft(1,1); - glm::vec2 texCoordBottomRight(0,0); - - if (_boxQuadID == GeometryCache::UNKNOWN_ID) { - _boxQuadID = DependencyManager::get()->allocateID(); - } - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} \ No newline at end of file diff --git a/interface/src/audio/AudioToolBox.h b/interface/src/audio/AudioToolBox.h deleted file mode 100644 index e07e6857aa..0000000000 --- a/interface/src/audio/AudioToolBox.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// AudioToolBox.h -// interface/src/audio -// -// Created by Stephen Birarda on 2014-12-16. -// 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_AudioToolBox_h -#define hifi_AudioToolBox_h - -#include -#include -#include - -class AudioToolBox : public Dependency { - SINGLETON_DEPENDENCY -public: - void render(int x, int y, int padding, bool boxed); - - bool mousePressEvent(int x, int y); -protected: - AudioToolBox(); -private: - gpu::TexturePointer _micTexture; - gpu::TexturePointer _muteTexture; - gpu::TexturePointer _boxTexture; - int _boxQuadID = GeometryCache::UNKNOWN_ID; - QRect _iconBounds; - qint64 _iconPulseTimeReference = 0; -}; - -#endif // hifi_AudioToolBox_h \ No newline at end of file diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 731c69979e..d0778481a6 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -107,6 +107,7 @@ Avatar::Avatar() : } Avatar::~Avatar() { + assert(_motionState == nullptr); for(auto attachment : _unusedAttachments) { delete attachment; } @@ -444,10 +445,10 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo _skeletonModel.renderJointCollisionShapes(0.7f); } - if (renderHead && shouldRenderHead(renderArgs, cameraPosition)) { + if (renderHead && shouldRenderHead(renderArgs)) { getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } - if (renderBounding && shouldRenderHead(renderArgs, cameraPosition)) { + if (renderBounding && shouldRenderHead(renderArgs)) { _skeletonModel.renderBoundingCollisionShapes(0.7f); } @@ -532,6 +533,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { + // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = Application::getInstance()->getMain3DScene(); @@ -580,7 +582,7 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } -bool Avatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const { +bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { return true; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index ecb16f6010..236b04864b 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -237,7 +237,7 @@ protected: Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const; void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const; virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f); - virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const; + virtual bool shouldRenderHead(const RenderArgs* renderArgs) const; virtual void fixupModelsInScene(); void simulateAttachments(float deltaTime); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 51fae3439a..88f550d68c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -114,6 +114,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // DO NOT update or fade out uninitialized Avatars ++avatarIterator; } else if (avatar->shouldDie()) { + removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); } else { @@ -163,12 +164,13 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe } // protected -void AvatarManager::removeAvatarMotionState(Avatar* avatar) { - AvatarMotionState* motionState= avatar->_motionState; +void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { + auto rawPointer = std::static_pointer_cast(avatar); + AvatarMotionState* motionState= rawPointer->_motionState; if (motionState) { // clean up physics stuff motionState->clearObjectBackPointer(); - avatar->_motionState = nullptr; + rawPointer->_motionState = nullptr; _avatarMotionStates.remove(motionState); _motionStatesToAdd.remove(motionState); _motionStatesToDelete.push_back(motionState); @@ -181,7 +183,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { if (avatarIterator != _avatarHash.end()) { std::shared_ptr avatar = std::dynamic_pointer_cast(avatarIterator.value()); if (avatar != _myAvatar && avatar->isInitialized()) { - removeAvatarMotionState(avatar.get()); + removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); _avatarHash.erase(avatarIterator); } @@ -197,7 +199,7 @@ void AvatarManager::clearOtherAvatars() { // don't remove myAvatar or uninitialized avatars from the list ++avatarIterator; } else { - removeAvatarMotionState(avatar.get()); + removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index b4482c5a34..9d1c94a47e 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -73,7 +73,7 @@ private: // virtual overrides virtual AvatarSharedPointer newSharedAvatar(); virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); - void removeAvatarMotionState(Avatar* avatar); + void removeAvatarMotionState(AvatarSharedPointer avatar); virtual void removeAvatar(const QUuid& sessionUUID); QVector _avatarFades; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 7d91e1f77f..a79a3cd8da 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -124,18 +124,25 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { if (!(_isFaceTrackerConnected || billboard)) { // Update eye saccades - const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; - const float AVERAGE_SACCADE_INTERVAL = 4.0f; + const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; + const float AVERAGE_SACCADE_INTERVAL = 6.0f; const float MICROSACCADE_MAGNITUDE = 0.002f; const float SACCADE_MAGNITUDE = 0.04f; - + const float MAXIMUM_SACCADE_SPEED = 0.8f; + if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { _saccadeTarget = SACCADE_MAGNITUDE * randVector(); } - _saccade += (_saccadeTarget - _saccade) * 0.50f; - + + glm::vec3 saccadeDelta = (_saccadeTarget - _saccade) * 0.5f; + float speed = glm::length(saccadeDelta) / deltaTime; + if (speed > MAXIMUM_SACCADE_SPEED) { + saccadeDelta = saccadeDelta * MAXIMUM_SACCADE_SPEED / speed; + } + _saccade += saccadeDelta; + // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9e998e7aeb..4b140e0569 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,6 @@ const float MyAvatar::ZOOM_DEFAULT = 1.5f; MyAvatar::MyAvatar() : Avatar(), - _turningKeyPressTime(0.0f), _gravity(0.0f, 0.0f, 0.0f), _wasPushing(false), _isPushing(false), @@ -97,8 +97,12 @@ MyAvatar::MyAvatar() : _feetTouchFloor(true), _isLookingAtLeftEye(true), _realWorldFieldOfView("realWorldFieldOfView", - DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES) + DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), + _firstPersonSkeletonModel(this), + _prevShouldDrawHead(true) { + _firstPersonSkeletonModel.setIsFirstPerson(true); + ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -132,6 +136,7 @@ QByteArray MyAvatar::toByteArray() { void MyAvatar::reset() { _skeletonModel.reset(); + _firstPersonSkeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -190,6 +195,7 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); + _firstPersonSkeletonModel.simulate(deltaTime); } if (!_skeletonModel.hasSkeleton()) { @@ -987,16 +993,35 @@ QString MyAvatar::getModelDescription() const { } void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { + Avatar::setFaceModelURL(faceModelURL); + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + getHead()->getFaceModel().setVisibleInScene(_prevShouldDrawHead, scene); _billboardValid = false; } void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { + Avatar::setSkeletonModelURL(skeletonModelURL); + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); _billboardValid = false; + + if (_useFullAvatar) { + _skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene); + + const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst"); + _firstPersonSkeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); + _firstPersonSkeletonModel.setVisibleInScene(!_prevShouldDrawHead, scene); + } else { + _skeletonModel.setVisibleInScene(true, scene); + + _firstPersonSkeletonModel.setVisibleInScene(false, scene); + _firstPersonSkeletonModel.reset(); + } } void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { + if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection, Q_ARG(const QUrl&, fullAvatarURL), @@ -1178,17 +1203,13 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) { if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { - return; // wait until both models are loaded + return; // wait until all models are loaded } - // check to see if when we added our models to the scene they were ready, if they were not ready, then - // fix them up in the scene fixupModelsInScene(); - const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition(); - // Render head so long as the camera isn't inside it - if (shouldRenderHead(renderArgs, cameraPos)) { + if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting); } if (postLighting) { @@ -1196,37 +1217,57 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo } } +void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) { + if (model->isActive() && model->isRenderable()) { + model->setVisibleInScene(visible, scene); + } +} + +void MyAvatar::preRender(RenderArgs* renderArgs) { + + render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + const bool shouldDrawHead = shouldRenderHead(renderArgs); + + _skeletonModel.initWhenReady(scene); + if (_useFullAvatar) { + _firstPersonSkeletonModel.initWhenReady(scene); + } + + if (shouldDrawHead != _prevShouldDrawHead) { + if (_useFullAvatar) { + if (shouldDrawHead) { + _skeletonModel.setVisibleInScene(true, scene); + _firstPersonSkeletonModel.setVisibleInScene(false, scene); + } else { + _skeletonModel.setVisibleInScene(false, scene); + _firstPersonSkeletonModel.setVisibleInScene(true, scene); + } + } else { + getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene); + } + + } + _prevShouldDrawHead = shouldDrawHead; +} + const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; -bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const { +bool MyAvatar::cameraInsideHead() const { const Head* head = getHead(); - return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || - (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); + const glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * _scale); +} + +bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { + return ((renderArgs->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) || + (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || + !cameraInsideHead()); } void MyAvatar::updateOrientation(float deltaTime) { - // Gather rotation information from keyboard - const float TIME_BETWEEN_HMD_TURNS = 0.5f; - const float HMD_TURN_DEGREES = 22.5f; - if (!qApp->isHMDMode()) { - // Smoothly rotate body with arrow keys if not in HMD - _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; - _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; - } else { - // Jump turns if in HMD - if (_driveKeys[ROT_RIGHT] || _driveKeys[ROT_LEFT]) { - if (_turningKeyPressTime == 0.0f) { - setOrientation(getOrientation() * - glm::quat(glm::radians(glm::vec3(0.f, _driveKeys[ROT_LEFT] ? HMD_TURN_DEGREES : -HMD_TURN_DEGREES, 0.0f)))); - } - _turningKeyPressTime += deltaTime; - if (_turningKeyPressTime > TIME_BETWEEN_HMD_TURNS) { - _turningKeyPressTime = 0.0f; - } - } else { - _turningKeyPressTime = 0.0f; - } - } + // Smoothly rotate body with arrow keys + _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; + _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); // update body orientation by movement inputs @@ -1468,7 +1509,7 @@ void MyAvatar::maybeUpdateBillboard() { } gpu::Context context(new gpu::GLBackend()); RenderArgs renderArgs(&context); - QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs); + QImage image = qApp->renderAvatarBillboard(&renderArgs); _billboard.clear(); QBuffer buffer(&_billboard); buffer.open(QIODevice::WriteOnly); @@ -1567,7 +1608,6 @@ void MyAvatar::renderLaserPointers() { //Gets the tip position for the laser pointer glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { - const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); glm::vec3 position = palm->getPosition(); @@ -1576,7 +1616,8 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { glm::vec3 result; - if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) { + const auto& compositor = qApp->getApplicationCompositor(); + if (compositor.calculateRayUICollisionPoint(position, direction, result)) { return result; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 618184cce8..2fea09ee27 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -35,11 +35,12 @@ public: void reset(); void update(float deltaTime); void simulate(float deltaTime); + void preRender(RenderArgs* renderArgs); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting = false) override; virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f) override; - virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const override; + virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void renderDebugBodyPoints(); // setters @@ -206,11 +207,14 @@ signals: private: + bool cameraInsideHead() const; + // These are made private for MyAvatar so that you will use the "use" methods instead virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); - float _turningKeyPressTime; + void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); + glm::vec3 _gravity; float _driveKeys[MAX_DRIVE_KEYS]; @@ -266,6 +270,10 @@ private: QString _headModelName; QString _bodyModelName; QString _fullAvatarModelName; + + // used for rendering when in first person view or when in an HMD. + SkeletonModel _firstPersonSkeletonModel; + bool _prevShouldDrawHead; }; #endif // hifi_MyAvatar_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2aa08d36f3..2f66e84b50 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -21,6 +21,7 @@ #include "Menu.h" #include "SkeletonModel.h" #include "Util.h" +#include "InterfaceLogging.h" enum StandingFootState { LEFT_FOOT, @@ -38,7 +39,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _standingFoot(NO_FOOT), _standingOffset(0.0f), _clampedFootPosition(0.0f), - _headClipDistance(DEFAULT_NEAR_CLIP) + _headClipDistance(DEFAULT_NEAR_CLIP), + _isFirstPerson(false) { assert(_owningAvatar); _enableShapes = true; @@ -98,7 +100,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setRotation(_owningAvatar->getOrientation() * refOrientation); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients()); - + Model::simulate(deltaTime, fullUpdate); if (!isActive() || !_owningAvatar->isMyAvatar()) { @@ -140,6 +142,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } + if (_isFirstPerson) { + cauterizeHead(); + updateClusterMatrices(); + } + _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } @@ -806,3 +813,57 @@ void SkeletonModel::renderBoundingCollisionShapes(float alpha) { bool SkeletonModel::hasSkeleton() { return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; } + +void SkeletonModel::initHeadBones() { + _headBones.clear(); + const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); + const int neckJointIndex = fbxGeometry.neckJointIndex; + std::queue q; + q.push(neckJointIndex); + _headBones.push_back(neckJointIndex); + + // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + while (q.size() > 0) { + int jointIndex = q.front(); + for (int i = 0; i < fbxGeometry.joints.size(); i++) { + const FBXJoint& fbxJoint = fbxGeometry.joints[i]; + if (jointIndex == fbxJoint.parentIndex) { + _headBones.push_back(i); + q.push(i); + } + } + q.pop(); + } +} + +void SkeletonModel::invalidateHeadBones() { + _headBones.clear(); +} + +void SkeletonModel::cauterizeHead() { + if (isActive()) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const int neckJointIndex = geometry.neckJointIndex; + if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) { + + // lazy init of headBones + if (_headBones.size() == 0) { + initHeadBones(); + } + + // preserve the translation for the neck + glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; + glm::vec4 zero(0, 0, 0, 0); + for (const int &i : _headBones) { + JointState& joint = _jointStates[i]; + glm::mat4 newXform(zero, zero, zero, trans); + joint.setTransform(newXform); + joint.setVisibleTransform(newXform); + } + } + } +} + +void SkeletonModel::onInvalidate() { + invalidateHeadBones(); +} diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index e28988326a..bffdc58659 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -112,6 +112,11 @@ public: float getHeadClipDistance() const { return _headClipDistance; } + void setIsFirstPerson(bool value) { _isFirstPerson = value; } + bool getIsFirstPerson() const { return _isFirstPerson; } + + virtual void onInvalidate() override; + signals: void skeletonLoaded(); @@ -132,7 +137,11 @@ protected: void maybeUpdateLeanRotation(const JointState& parentState, JointState& state); void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - + + void cauterizeHead(); + void initHeadBones(); + void invalidateHeadBones(); + private: void renderJointConstraints(int jointIndex); @@ -164,6 +173,9 @@ private: glm::vec3 _clampedFootPosition; float _headClipDistance; // Near clip distance to use if no separate head model + + bool _isFirstPerson; + std::vector _headBones; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp deleted file mode 100644 index 27cee5185b..0000000000 --- a/interface/src/devices/CameraToolBox.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// -// CameraToolBox.cpp -// interface/src/devices -// -// Created by David Rowe on 30 Apr 2015. -// 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 "InterfaceConfig.h" - -#include -#include - -#include "gpu/GLBackend.h" -#include "Application.h" -#include "CameraToolBox.h" -#include "FaceTracker.h" - - -CameraToolBox::CameraToolBox() : - _iconPulseTimeReference(usecTimestampNow()), - _doubleClickTimer(NULL) -{ -} - -CameraToolBox::~CameraToolBox() { - if (_doubleClickTimer) { - _doubleClickTimer->stop(); - delete _doubleClickTimer; - } -} - -bool CameraToolBox::mousePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - if (!_doubleClickTimer) { - // Toggle mute after waiting to check that it's not a double-click. - const int DOUBLE_CLICK_WAIT = 200; // ms - _doubleClickTimer = new QTimer(this); - connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute())); - _doubleClickTimer->setSingleShot(true); - _doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT); - _doubleClickTimer->start(); - } - return true; - } - return false; -} - -bool CameraToolBox::mouseDoublePressEvent(int x, int y) { - if (_iconBounds.contains(x, y)) { - if (_doubleClickTimer) { - _doubleClickTimer->stop(); - delete _doubleClickTimer; - _doubleClickTimer = NULL; - } - Application::getInstance()->resetSensors(); - return true; - } - return false; -} - -void CameraToolBox::toggleMute() { - delete _doubleClickTimer; - _doubleClickTimer = NULL; - - FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); - if (faceTracker) { - faceTracker->toggleMute(); - } -} - -void CameraToolBox::render(int x, int y, bool boxed) { - glEnable(GL_TEXTURE_2D); - - if (!_enabledTexture) { - _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); - } - if (!_mutedTexture) { - _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); - } - - const int MUTE_ICON_SIZE = 24; - _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); - float iconColor = 1.0f; - if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture)); - } else { - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture)); - - // Make muted icon pulsate - static const float PULSE_MIN = 0.4f; - static const float PULSE_MAX = 1.0f; - static const float PULSE_FREQUENCY = 1.0f; // in Hz - qint64 now = usecTimestampNow(); - if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { - // Prevents t from getting too big, which would diminish glm::cos precision - _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); - } - float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; - float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; - iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; - } - - glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); - - glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); - glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); - glm::vec2 texCoordTopLeft(1,1); - glm::vec2 texCoordBottomRight(0,0); - - if (_boxQuadID == GeometryCache::UNKNOWN_ID) { - _boxQuadID = DependencyManager::get()->allocateID(); - } - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); - - glDisable(GL_TEXTURE_2D); -} \ No newline at end of file diff --git a/interface/src/devices/CameraToolBox.h b/interface/src/devices/CameraToolBox.h deleted file mode 100644 index 89e0c6a8dc..0000000000 --- a/interface/src/devices/CameraToolBox.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// CameraToolBox.h -// interface/src/devices -// -// Created by David Rowe on 30 Apr 2015. -// 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_CameraToolBox_h -#define hifi_CameraToolBox_h - -#include - -#include -#include - -class CameraToolBox : public QObject, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - void render(int x, int y, bool boxed); - bool mousePressEvent(int x, int y); - bool mouseDoublePressEvent(int x, int y); - -protected: - CameraToolBox(); - ~CameraToolBox(); - -private slots: - void toggleMute(); - -private: - gpu::TexturePointer _enabledTexture; - gpu::TexturePointer _mutedTexture; - int _boxQuadID = GeometryCache::UNKNOWN_ID; - QRect _iconBounds; - qint64 _iconPulseTimeReference = 0; - QTimer* _doubleClickTimer; -}; - -#endif // hifi_CameraToolBox_h \ No newline at end of file diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index 07f3c4873c..91f3b98bac 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -17,7 +17,7 @@ #include "Joystick.h" -const float CONTROLLER_THRESHOLD = 0.25f; +const float CONTROLLER_THRESHOLD = 0.3f; #ifdef HAVE_SDL2 const float MAX_AXIS = 32768.0f; @@ -173,9 +173,7 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { // 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); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); // Zoom mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); @@ -203,13 +201,16 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { // 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); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_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); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A)); #endif } diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index 9fadf7be82..e04c27ae88 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -276,7 +276,10 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); #endif - + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space)); + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T)); } float KeyboardMouseDevice::getButton(int channel) const { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 0c5f780560..9d7146cbe7 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -11,10 +11,10 @@ // #include "InterfaceConfig.h" - #include "OculusManager.h" #include "ui/overlays/Text3DOverlay.h" +#include #include #include #include @@ -37,7 +37,6 @@ #include "InterfaceLogging.h" #include "Application.h" - template void for_each_eye(Function function) { for (ovrEyeType eye = ovrEyeType::ovrEye_Left; @@ -54,6 +53,71 @@ void for_each_eye(const ovrHmd & hmd, Function function) { function(eye); } } +enum CalibrationState { + UNCALIBRATED, + WAITING_FOR_DELTA, + WAITING_FOR_ZERO, + WAITING_FOR_ZERO_HELD, + CALIBRATED +}; + +inline glm::mat4 toGlm(const ovrMatrix4f & om) { + return glm::transpose(glm::make_mat4(&om.M[0][0])); +} + +inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { + return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); +} + +inline glm::vec3 toGlm(const ovrVector3f & ov) { + return glm::make_vec3(&ov.x); +} + +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); +} + +inline glm::quat toGlm(const ovrQuatf & oq) { + return glm::make_quat(&oq.x); +} + +inline glm::mat4 toGlm(const ovrPosef & op) { + glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); + glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); + return translation * orientation; +} + +inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { + ovrMatrix4f result; + glm::mat4 transposed(glm::transpose(m)); + memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); + return result; +} + +inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { + return{ v.x, v.y, v.z }; +} + +inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { + return{ v.x, v.y }; +} + +inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { + return{ (int)v.x, (int)v.y }; +} + +inline ovrQuatf ovrFromGlm(const glm::quat & q) { + return{ q.x, q.y, q.z, q.w }; +} + #ifdef Q_OS_WIN @@ -171,54 +235,54 @@ private: } }; -SwapFramebufferWrapper* OculusManager::_swapFbo{ nullptr }; -MirrorFramebufferWrapper* OculusManager::_mirrorFbo{ nullptr }; -ovrLayerEyeFov OculusManager::_sceneLayer; +static SwapFramebufferWrapper* _swapFbo{ nullptr }; +static MirrorFramebufferWrapper* _mirrorFbo{ nullptr }; +static ovrLayerEyeFov _sceneLayer; #else -ovrTexture OculusManager::_eyeTextures[ovrEye_Count]; -GlWindow* OculusManager::_outputWindow{ nullptr }; +static ovrTexture _eyeTextures[ovrEye_Count]; +static GlWindow* _outputWindow{ nullptr }; #endif -bool OculusManager::_isConnected = false; -ovrHmd OculusManager::_ovrHmd; -ovrFovPort OculusManager::_eyeFov[ovrEye_Count]; -ovrVector3f OculusManager::_eyeOffset[ovrEye_Count]; -ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count]; -ovrSizei OculusManager::_renderTargetSize; -glm::mat4 OculusManager::_eyeProjection[ovrEye_Count]; -unsigned int OculusManager::_frameIndex = 0; -bool OculusManager::_frameTimingActive = false; -Camera* OculusManager::_camera = NULL; -ovrEyeType OculusManager::_activeEye = ovrEye_Count; -bool OculusManager::_hswDismissed = false; +static bool _isConnected = false; +static ovrHmd _ovrHmd; +static ovrFovPort _eyeFov[ovrEye_Count]; +static ovrVector3f _eyeOffset[ovrEye_Count]; +static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count]; +static ovrSizei _renderTargetSize; +static glm::mat4 _eyeProjection[ovrEye_Count]; +static unsigned int _frameIndex = 0; +static bool _frameTimingActive = false; +static Camera* _camera = NULL; +static ovrEyeType _activeEye = ovrEye_Count; +static bool _hswDismissed = false; -float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; -float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE; -float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; -float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; -quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec -float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f; -OculusManager::CalibrationState OculusManager::_calibrationState; -glm::vec3 OculusManager::_calibrationPosition; -glm::quat OculusManager::_calibrationOrientation; -quint64 OculusManager::_calibrationStartTime; -int OculusManager::_calibrationMessage = 0; -glm::vec3 OculusManager::_eyePositions[ovrEye_Count]; +static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; +static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE; +static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; +static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; +static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec +static const float CALIBRATION_MESSAGE_DISTANCE = 2.5f; +static CalibrationState _calibrationState; +static glm::vec3 _calibrationPosition; +static glm::quat _calibrationOrientation; +static quint64 _calibrationStartTime; +static int _calibrationMessage = 0; +static glm::vec3 _eyePositions[ovrEye_Count]; // TODO expose this as a developer toggle -bool OculusManager::_eyePerFrameMode = false; -ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count; -ovrSizei OculusManager::_recommendedTexSize = { 0, 0 }; -float OculusManager::_offscreenRenderScale = 1.0; -ovrRecti OculusManager::_eyeViewports[ovrEye_Count]; +static bool _eyePerFrameMode = false; +static ovrEyeType _lastEyeRendered = ovrEye_Count; +static ovrSizei _recommendedTexSize = { 0, 0 }; +static float _offscreenRenderScale = 1.0; +static glm::mat4 _combinedProjection; +static ovrPosef _eyeRenderPoses[ovrEye_Count]; +static ovrRecti _eyeViewports[ovrEye_Count]; +static ovrVector3f _eyeOffsets[ovrEye_Count]; -void OculusManager::init() { -} - -void OculusManager::deinit() { -} +glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; } +glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; } void OculusManager::connect(QOpenGLContext* shareContext) { qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING; @@ -269,7 +333,15 @@ void OculusManager::connect(QOpenGLContext* shareContext) { for_each_eye([&](ovrEyeType eye) { _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; + _eyeProjection[eye] = toGlm(ovrMatrix4f_Projection(_eyeFov[eye], + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); + ovrEyeRenderDesc erd = ovrHmd_GetRenderDesc(_ovrHmd, eye, _eyeFov[eye]); + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; }); + ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0]; + combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan; + _combinedProjection = toGlm(ovrMatrix4f_Projection(combinedFov, + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; @@ -387,7 +459,7 @@ void OculusManager::disconnect() { } } -void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { +void positionCalibrationBillboard(Text3DOverlay* billboard) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); glm::quat headOrientation = myAvatar->getHeadOrientation(); headOrientation.x = 0; @@ -398,7 +470,7 @@ void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { billboard->setRotation(headOrientation); } -void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { +void calibrate(const glm::vec3& position, const glm::quat& orientation) { static QString instructionMessage = "Hold still to calibrate"; static QString progressMessage; static Text3DOverlay* billboard; @@ -528,18 +600,12 @@ void OculusManager::endFrameTiming() { //Sets the camera FoV and aspect ratio void OculusManager::configureCamera(Camera& camera) { - ovrFovPort fov; if (_activeEye == ovrEye_Count) { // When not rendering, provide a FOV encompasing both eyes - fov = _eyeFov[0]; - fov.RightTan = _eyeFov[1].RightTan; - } else { - // When rendering, provide the exact FOV - fov = _eyeFov[_activeEye]; - } - // Convert the FOV to the correct projection matrix - glm::mat4 projection = toGlm(ovrMatrix4f_Projection(fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); - camera.setProjection(projection); + camera.setProjection(_combinedProjection); + return; + } + camera.setProjection(_eyeProjection[_activeEye]); } //Displays everything for the oculus, frame timing must be active @@ -609,13 +675,11 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const } trackerPosition = bodyOrientation * trackerPosition; - static ovrVector3f eyeOffsets[2] = { { 0, 0, 0 }, { 0, 0, 0 } }; ovrPosef eyePoses[ovrEye_Count]; - ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, eyeOffsets, eyePoses, nullptr); + 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){ // If we're in eye-per-frame mode, only render one eye @@ -625,29 +689,17 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const return; } _lastEyeRendered = _activeEye = eye; - eyeRenderPose[eye] = eyePoses[eye]; + _eyeRenderPoses[eye] = eyePoses[eye]; // Set the camera rotation for this eye - orientation.x = eyeRenderPose[eye].Orientation.x; - orientation.y = eyeRenderPose[eye].Orientation.y; - orientation.z = eyeRenderPose[eye].Orientation.z; - orientation.w = eyeRenderPose[eye].Orientation.w; - - // Update the application camera with the latest HMD position - whichCamera.setHmdPosition(trackerPosition); - whichCamera.setHmdRotation(orientation); - + vec3 eyePosition = toGlm(_eyeRenderPoses[eye].Position); + eyePosition = whichCamera.getRotation() * eyePosition; + quat eyeRotation = toGlm(_eyeRenderPoses[eye].Orientation); + // Update our camera to what the application camera is doing - _camera->setRotation(whichCamera.getRotation()); - _camera->setPosition(whichCamera.getPosition()); + _camera->setRotation(whichCamera.getRotation() * eyeRotation); + _camera->setPosition(whichCamera.getPosition() + eyePosition); configureCamera(*_camera); - - // Store the latest left and right eye render locations for things that need to know - glm::vec3 thisEyePosition = position + trackerPosition + - (bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) * - glm::vec3(_eyeRenderDesc[eye].HmdToEyeViewOffset.x, _eyeRenderDesc[eye].HmdToEyeViewOffset.y, _eyeRenderDesc[eye].HmdToEyeViewOffset.z)); - - _eyePositions[eye] = thisEyePosition; _camera->update(1.0f / Application::getInstance()->getFps()); glMatrixMode(GL_PROJECTION); @@ -662,8 +714,8 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); renderArgs->_renderSide = RenderArgs::MONO; - qApp->displaySide(renderArgs, *_camera, false); - qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera); + qApp->displaySide(renderArgs, *_camera); + qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye); }); _activeEye = ovrEye_Count; @@ -709,7 +761,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const // Submit the frame to the Oculus SDK for timewarp and distortion for_each_eye([&](ovrEyeType eye) { - _sceneLayer.RenderPose[eye] = eyeRenderPose[eye]; + _sceneLayer.RenderPose[eye] = _eyeRenderPoses[eye]; }); auto header = &_sceneLayer.Header; ovrResult res = ovrHmd_SubmitFrame(_ovrHmd, _frameIndex, nullptr, &header, 1); @@ -725,6 +777,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const // rendering to complete before it starts the distortion rendering, // but without triggering a CPU/GPU synchronization glWaitSync(syncObject, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(syncObject); GLuint textureId = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)); for_each_eye([&](ovrEyeType eye) { @@ -734,10 +787,21 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const }); // restore our normal viewport - ovrHmd_EndFrame(_ovrHmd, eyeRenderPose, _eyeTextures); + ovrHmd_EndFrame(_ovrHmd, _eyeRenderPoses, _eyeTextures); glCanvas->makeCurrent(); #endif - + + + // in order to account account for changes in the pick ray caused by head movement + // we need to force a mouse move event on every frame (perhaps we could change this + // to based on the head moving a minimum distance from the last position in which we + // sent?) + { + QMouseEvent mouseEvent(QEvent::MouseMove, glCanvas->mapFromGlobal(QCursor::pos()), + Qt::NoButton, Qt::NoButton, 0); + qApp->mouseMoveEvent(&mouseEvent, 0); + } + } @@ -835,3 +899,15 @@ int OculusManager::getHMDScreen() { #endif } +mat4 OculusManager::getEyeProjection(int eye) { + return _eyeProjection[eye]; +} + +mat4 OculusManager::getEyePose(int eye) { + return toGlm(_eyeRenderPoses[eye]); +} + +mat4 OculusManager::getHeadPose() { + ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); + return toGlm(ts.HeadPose.ThePose); +} \ No newline at end of file diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index b82379a7b3..9d9f091296 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -13,8 +13,6 @@ #ifndef hifi_OculusManager_h #define hifi_OculusManager_h -#include - #include #include #include @@ -23,23 +21,11 @@ #include "RenderArgs.h" class QOpenGLContext; - class Camera; -class GlWindow; -class PalmData; -class Text3DOverlay; - -#ifdef Q_OS_WIN -struct SwapFramebufferWrapper; -struct MirrorFramebufferWrapper; -#endif - /// Handles interaction with the Oculus Rift. class OculusManager { public: - static void init(); - static void deinit(); static void connect(QOpenGLContext* shareContext); static void disconnect(); static bool isConnected(); @@ -59,121 +45,14 @@ public: static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane); - static glm::vec3 getLeftEyePosition() { return _eyePositions[ovrEye_Left]; } - static glm::vec3 getRightEyePosition() { return _eyePositions[ovrEye_Right]; } + static glm::vec3 getLeftEyePosition(); + static glm::vec3 getRightEyePosition(); static int getHMDScreen(); - -private: - static void initSdk(); - static void shutdownSdk(); - static bool _isConnected; - static glm::vec3 _eyePositions[ovrEye_Count]; - static ovrHmd _ovrHmd; - static ovrFovPort _eyeFov[ovrEye_Count]; - 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; - static Camera* _camera; - static ovrEyeType _activeEye; - static bool _hswDismissed; - - static void calibrate(const glm::vec3 position, const glm::quat orientation); - enum CalibrationState { - UNCALIBRATED, - WAITING_FOR_DELTA, - WAITING_FOR_ZERO, - WAITING_FOR_ZERO_HELD, - CALIBRATED - }; - static void positionCalibrationBillboard(Text3DOverlay* message); - static float CALIBRATION_DELTA_MINIMUM_LENGTH; - static float CALIBRATION_DELTA_MINIMUM_ANGLE; - static float CALIBRATION_ZERO_MAXIMUM_LENGTH; - static float CALIBRATION_ZERO_MAXIMUM_ANGLE; - static quint64 CALIBRATION_ZERO_HOLD_TIME; - static float CALIBRATION_MESSAGE_DISTANCE; - static CalibrationState _calibrationState; - static glm::vec3 _calibrationPosition; - static glm::quat _calibrationOrientation; - static quint64 _calibrationStartTime; - static int _calibrationMessage; - // TODO drop this variable and use the existing 'Developer | Render | Scale Resolution' value - static ovrSizei _recommendedTexSize; - 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 + static glm::mat4 getEyeProjection(int eye); + static glm::mat4 getEyePose(int eye); + static glm::mat4 getHeadPose(); }; - -inline glm::mat4 toGlm(const ovrMatrix4f & om) { - return glm::transpose(glm::make_mat4(&om.M[0][0])); -} - -inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { - return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); -} - -inline glm::vec3 toGlm(const ovrVector3f & ov) { - return glm::make_vec3(&ov.x); -} - -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); -} - -inline glm::quat toGlm(const ovrQuatf & oq) { - return glm::make_quat(&oq.x); -} - -inline glm::mat4 toGlm(const ovrPosef & op) { - glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); - glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); - return translation * orientation; -} - -inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { - ovrMatrix4f result; - glm::mat4 transposed(glm::transpose(m)); - memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); - return result; -} - -inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { - return{ v.x, v.y, v.z }; -} - -inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { - return{ v.x, v.y }; -} - -inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { - return{ (int)v.x, (int)v.y }; -} - -inline ovrQuatf ovrFromGlm(const glm::quat & q) { - return{ q.x, q.y, q.z, q.w }; -} - #endif // hifi_OculusManager_h diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 32c5a7c157..12b6f4263b 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -36,6 +36,8 @@ const float NECK_X = 0.25f; // meters const float NECK_Y = 0.3f; // meters const float NECK_Z = 0.3f; // meters +const float CONTROLLER_THRESHOLD = 0.35f; + #ifdef __APPLE__ typedef int (*SixenseBaseFunction)(); typedef int (*SixenseTakeIntFunction)(int); @@ -326,6 +328,12 @@ void SixenseManager::update(float deltaTime) { } _controllersAtBase = (numControllersAtBase == 2); } + + for (auto axisState : _axisStateMap) { + if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { + _axisStateMap[axisState.first] = 0.0f; + } + } #endif // HAVE_SIXENSE } @@ -512,7 +520,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { - pos = qApp->getApplicationOverlay().getPalmClickLocation(palm); + pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); @@ -724,6 +732,13 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); + } float SixenseManager::getButton(int channel) const { diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 09edb03e5a..41e549a861 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -12,6 +12,7 @@ #include "InterfaceConfig.h" #include +#include #include #include "gpu/GLBackend.h" @@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { _activeEye = &eye; glViewport(portalX, portalY, portalW, portalH); glScissor(portalX, portalY, portalW, portalH); + + glm::mat4 projection = glm::frustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); + float fov = atan(1.0f / projection[1][1]); + projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0)); + eyeCamera.setProjection(projection); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix - glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum - GLfloat p[4][4]; - // Really? - glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); - float cotangent = p[1][1]; - GLfloat fov = atan(1.0f / cotangent); - glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax - + glLoadMatrixf(glm::value_ptr(projection)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); + qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 13ca17355b..e591034fb5 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -23,7 +23,7 @@ #ifdef Q_OS_WIN static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) { const UINT TIMEOUT = 200; // ms - DWORD response; + DWORD_PTR response; LRESULT result = SendMessageTimeout(hWnd, UWM_IDENTIFY_INSTANCES, 0, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, TIMEOUT, &response); if (result == 0) { // Timeout; continue search. return TRUE; diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 851c523c5d..7a3f4a99b5 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -28,6 +28,7 @@ ControllerScriptingInterface::ControllerScriptingInterface() : { } + static int actionMetaTypeId = qRegisterMetaType(); static int inputChannelMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 392891c676..f15ee43ca3 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -79,6 +79,7 @@ public: bool isMouseCaptured() const { return _mouseCaptured; } bool isTouchCaptured() const { return _touchCaptured; } bool isWheelCaptured() const { return _wheelCaptured; } + bool areActionsCaptured() const { return _actionsCaptured; } bool isJoystickCaptured(int joystickIndex) const; void updateInputControllers(); @@ -123,6 +124,9 @@ public slots: virtual void captureWheelEvents() { _wheelCaptured = true; } virtual void releaseWheelEvents() { _wheelCaptured = false; } + + virtual void captureActionEvents() { _actionsCaptured = true; } + virtual void releaseActionEvents() { _actionsCaptured = false; } virtual void captureJoystick(int joystickIndex); virtual void releaseJoystick(int joystickIndex); @@ -143,6 +147,7 @@ private: bool _mouseCaptured; bool _touchCaptured; bool _wheelCaptured; + bool _actionsCaptured; QMultiMap _capturedKeys; QSet _capturedJoysticks; diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index f258fd695c..f65d638ccc 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -25,9 +25,9 @@ bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); - ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + const auto& compositor = Application::getInstance()->getApplicationCompositor(); - return applicationOverlay.calculateRayUICollisionPoint(position, direction, result); + return compositor.calculateRayUICollisionPoint(position, direction, result); } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { @@ -40,7 +40,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* conte glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); glm::vec3 eulers = ::safeEulerAngles(rotation); - return qScriptValueFromValue(engine, Application::getInstance()->getApplicationOverlay() + return qScriptValueFromValue(engine, Application::getInstance()->getApplicationCompositor() .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); } return QScriptValue::NullValue; diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 9b0f8dc4d1..4bcced1fa2 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -27,11 +27,11 @@ public: static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); public slots: - void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); }; + void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); }; private: HMDScriptingInterface() {}; - bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); }; + bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); }; bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } bool getHUDLookAtPosition3D(glm::vec3& result) const; diff --git a/interface/src/starfield/renderer/Renderer.cpp b/interface/src/starfield/renderer/Renderer.cpp index 1ebb4245e2..5fc858d952 100644 --- a/interface/src/starfield/renderer/Renderer.cpp +++ b/interface/src/starfield/renderer/Renderer.cpp @@ -11,6 +11,7 @@ // #include "starfield/renderer/Renderer.h" +#include "Application.h" using namespace starfield; @@ -52,6 +53,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, matrix[3][1] = 0.0f; matrix[3][2] = 0.0f; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection())); + glMatrixMode(GL_MODELVIEW); // extract local z vector vec3 ahead = vec3(matrix[2]); @@ -74,6 +79,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray)); this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } // renderer construction diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp new file mode 100644 index 0000000000..7b55de6dd8 --- /dev/null +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -0,0 +1,763 @@ +// +// ApplicationCompositor.cpp +// interface/src/ui/overlays +// +// Created by Benjamin Arnold on 5/27/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "InterfaceConfig.h" + +#include "ApplicationCompositor.h" + +#include + +#include +#include +#include +#include + +#include "Application.h" + + +// Used to animate the magnification windows +static const float MAG_SPEED = 0.08f; + +static const quint64 MSECS_TO_USECS = 1000ULL; +static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; + +static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; +static const float reticleSize = TWO_PI / 100.0f; + +static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; +static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; + +static const float CURSOR_PIXEL_SIZE = 32.0f; +static const float MOUSE_PITCH_RANGE = 1.0f * PI; +static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; +static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); + +static gpu::BufferPointer _hemiVertices; +static gpu::BufferPointer _hemiIndices; +static int _hemiIndexCount{ 0 }; +EntityItemID ApplicationCompositor::_noItemId; +static QString _tooltipId; + +// Return a point's cartesian coordinates on a sphere from pitch and yaw +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))); +} + +//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should +//be multiplied by dir and added to origin to get the location of the collision +bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) +{ + //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection + + //Compute A, B and C coefficients + float a = glm::dot(dir, dir); + float b = 2 * glm::dot(dir, origin); + float c = glm::dot(origin, origin) - (r * r); + + //Find discriminant + float disc = b * b - 4 * a * c; + + // if discriminant is negative there are no real roots, so return + // false as ray misses sphere + if (disc < 0) { + return false; + } + + // compute q as described above + float distSqrt = sqrtf(disc); + float q; + if (b < 0) { + q = (-b - distSqrt) / 2.0f; + } else { + q = (-b + distSqrt) / 2.0f; + } + + // compute t0 and t1 + float t0 = q / a; + float t1 = c / q; + + // make sure t0 is smaller than t1 + if (t0 > t1) { + // if t0 is bigger than t1 swap them around + float temp = t0; + t0 = t1; + t1 = temp; + } + + // if t1 is less than zero, the object is in the ray's negative direction + // and consequently the ray misses the sphere + if (t1 < 0) { + return false; + } + + // if t0 is less than zero, the intersection point is at t1 + if (t0 < 0) { + *result = t1; + return true; + } else { // else the intersection point is at t0 + *result = t0; + return true; + } +} + +ApplicationCompositor::ApplicationCompositor() { + memset(_reticleActive, 0, sizeof(_reticleActive)); + memset(_magActive, 0, sizeof(_reticleActive)); + memset(_magSizeMult, 0, sizeof(_magSizeMult)); + + auto geometryCache = DependencyManager::get(); + + _reticleQuad = geometryCache->allocateID(); + _magnifierQuad = geometryCache->allocateID(); + _magnifierBorder = geometryCache->allocateID(); + + auto entityScriptingInterface = DependencyManager::get(); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_hoverItemId != entityItemID) { + _hoverItemId = entityItemID; + _hoverItemEnterUsecs = usecTimestampNow(); + auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId); + _hoverItemHref = properties.getHref(); + auto cursor = Cursor::Manager::instance().getCursor(); + if (!_hoverItemHref.isEmpty()) { + cursor->setIcon(Cursor::Icon::LINK); + } else { + cursor->setIcon(Cursor::Icon::DEFAULT); + } + } + }); + + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + if (_hoverItemId == entityItemID) { + _hoverItemId = _noItemId; + _hoverItemHref.clear(); + auto cursor = Cursor::Manager::instance().getCursor(); + cursor->setIcon(Cursor::Icon::DEFAULT); + if (!_tooltipId.isEmpty()) { + qDebug() << "Closing tooltip " << _tooltipId; + Tooltip::closeTip(_tooltipId); + _tooltipId.clear(); + } + } + }); +} + +ApplicationCompositor::~ApplicationCompositor() { +} + + +void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { + auto& cursorManager = Cursor::Manager::instance(); + auto cursor = cursorManager.getCursor(cursorIndex); + auto iconId = cursor->getIcon(); + if (!_cursors.count(iconId)) { + auto iconPath = cursorManager.getIconImage(cursor->getIcon()); + _cursors[iconId] = DependencyManager::get()-> + getImageTexture(iconPath); + } + batch.setUniformTexture(0, _cursors[iconId]); +} + +// Draws the FBO texture for the screen +void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { + if (_alpha == 0.0f) { + return; + } + + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); + if (!texture) { + return; + } + + updateTooltips(); + + auto deviceSize = qApp->getDeviceSize(); + glViewport(0, 0, deviceSize.width(), deviceSize.height()); + + //Handle fading and deactivation/activation of UI + gpu::Batch batch; + + renderArgs->_context->syncCache(); + auto geometryCache = DependencyManager::get(); + + geometryCache->useSimpleDrawPipeline(batch); + batch.setModelTransform(Transform()); + batch.setViewTransform(Transform()); + batch.setProjectionTransform(mat4()); + batch._glBindTexture(GL_TEXTURE_2D, texture); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + + // Doesn't actually render + renderPointers(batch); + + //draw the mouse pointer + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 canvasSize = qApp->getCanvasSize(); + vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); + // Invert the Y axis + mousePosition.y *= -1.0f; + + Transform model; + model.setTranslation(vec3(mousePosition, 0)); + vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + geometryCache->renderUnitQuad(batch, vec4(1)); + renderArgs->_context->render(batch); +} + + +vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; +} + +// Draws the FBO texture for Oculus rift. +void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) { + if (_alpha == 0.0f) { + return; + } + + GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); + if (!texture) { + return; + } + + updateTooltips(); + + vec2 canvasSize = qApp->getCanvasSize(); + _textureAspectRatio = aspect(canvasSize); + + renderArgs->_context->syncCache(); + auto geometryCache = DependencyManager::get(); + + gpu::Batch batch; + geometryCache->useSimpleDrawPipeline(batch); + batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_CULL_FACE); + batch._glBindTexture(GL_TEXTURE_2D, texture); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + batch.setViewTransform(Transform()); + batch.setProjectionTransform(qApp->getEyeProjection(eye)); + + mat4 eyePose = qApp->getEyePose(eye); + glm::mat4 overlayXfm = glm::inverse(eyePose); + +#ifdef DEBUG_OVERLAY + { + batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2))); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + } +#else + { + batch.setModelTransform(overlayXfm); + drawSphereSection(batch); + } +#endif + + // Doesn't actually render + renderPointers(batch); + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); + + bindCursorTexture(batch); + + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + //Controller Pointers + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + // Render reticle at location + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + } + + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, reticleScale); + batch.setModelTransform(reticleXfm); + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + + renderArgs->_context->render(batch); +} + + +void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { + cursorPos *= qApp->getCanvasSize(); + const glm::vec2 projection = screenToSpherical(cursorPos); + // The overlay space orientation of the mouse coordinates + const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); + // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. + // Now we need to account for the actual camera position relative to the overlay + glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); + + + // We need the RAW camera orientation and position, because this is what the overlay is + // rendered relative to + const glm::vec3 overlayPosition = qApp->getCamera()->getPosition(); + const glm::quat overlayOrientation = qApp->getCamera()->getRotation(); + + // Intersection UI overlay space + glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; + glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition; + glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition; + + // Intersection in world space + origin = worldSpaceHeadPosition; + direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition); +} + +//Caculate the click location using one of the sixense controllers. Scale is not applied +QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const { + QPoint rv; + auto canvasSize = qApp->getCanvasSize(); + if (qApp->isHMDMode()) { + glm::vec2 polar = getPolarCoordinates(*palm); + glm::vec2 point = sphericalToScreen(-polar); + rv.rx() = point.x; + rv.ry() = point.y; + } else { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + glm::dmat4 projection; + qApp->getProjectionMatrix(&projection); + glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); + glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); + glm::vec3 tipPos = invOrientation * (tip - eyePos); + + glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); + glm::vec3 ndcSpacePos; + if (clipSpacePos.w != 0) { + ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; + } + + rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); + rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); + } + return rv; +} + +//Finds the collision point of a world space ray +bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + + glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); + + glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); + glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); + + float t; + if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ + result = position + direction * t; + return true; + } + + return false; +} + +//Renders optional pointers +void ApplicationCompositor::renderPointers(gpu::Batch& batch) { + if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { + //If we are in oculus, render reticle later + QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); + _reticlePosition[MOUSE] = position; + _reticleActive[MOUSE] = true; + _magActive[MOUSE] = _magnifier; + _reticleActive[LEFT_CONTROLLER] = false; + _reticleActive[RIGHT_CONTROLLER] = false; + } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + //only render controller pointer if we aren't already rendering a mouse pointer + _reticleActive[MOUSE] = false; + _magActive[MOUSE] = false; + renderControllerPointers(batch); + } +} + + +void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + + //Static variables used for storing controller state + static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; + static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; + static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; + + const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); + + for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { + const int index = palmIndex - 1; + + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + continue; + } + + int controllerButtons = palmData->getControllerButtons(); + + //Check for if we should toggle or drag the magnification window + if (controllerButtons & BUTTON_3) { + if (isPressed[index] == false) { + //We are now dragging the window + isPressed[index] = true; + //set the pressed time in us + pressedTime[index] = usecTimestampNow(); + stateWhenPressed[index] = _magActive[index]; + } + } else if (isPressed[index]) { + isPressed[index] = false; + //If the button was only pressed for < 250 ms + //then disable it. + + const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; + if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { + _magActive[index] = !stateWhenPressed[index]; + } + } + + //if we have the oculus, we should make the cursor smaller since it will be + //magnified + if (qApp->isHMDMode()) { + + QPoint point = getPalmClickLocation(palmData); + + _reticlePosition[index] = point; + + //When button 2 is pressed we drag the mag window + if (isPressed[index]) { + _magActive[index] = true; + } + + // If oculus is enabled, we draw the crosshairs later + continue; + } + + auto canvasSize = qApp->getCanvasSize(); + int mouseX, mouseY; + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + QPoint res = getPalmClickLocation(palmData); + mouseX = res.x(); + mouseY = res.y(); + } else { + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + + // Get the pixel range over which the xAngle and yAngle are scaled + float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); + + mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); + mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); + } + + //If the cursor is out of the screen then don't render it + if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { + _reticleActive[index] = false; + continue; + } + _reticleActive[index] = true; + + + const float reticleSize = 40.0f; + + mouseX -= reticleSize / 2.0f; + mouseY += reticleSize / 2.0f; + + + glm::vec2 topLeft(mouseX, mouseY); + glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); + glm::vec2 texCoordTopLeft(0.0f, 0.0f); + glm::vec2 texCoordBottomRight(1.0f, 1.0f); + + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); + + } +} + +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder) { + if (!_magnifier) { + return; + } + auto canvasSize = qApp->getCanvasSize(); + + const int widgetWidth = canvasSize.x; + const int widgetHeight = canvasSize.y; + + const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; + const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; + // Magnification Texture Coordinates + const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; + const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; + const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; + const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; + + const float newHalfWidth = halfWidth * MAGNIFY_MULT; + const float newHalfHeight = halfHeight * MAGNIFY_MULT; + //Get yaw / pitch value for the corners + const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, + magPos.y - newHalfHeight)); + const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, + magPos.y + newHalfHeight)); + + const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); + const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); + const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); + const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); + + auto geometryCache = DependencyManager::get(); + + if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight + || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { + QVector border; + border << topLeft; + border << bottomLeft; + border << bottomRight; + border << topRight; + border << topLeft; + geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); + + _previousMagnifierBottomLeft = bottomLeft; + _previousMagnifierBottomRight = bottomRight; + _previousMagnifierTopLeft = topLeft; + _previousMagnifierTopRight = topRight; + } + + glPushMatrix(); { + if (showBorder) { + glDisable(GL_TEXTURE_2D); + glLineWidth(1.0f); + //Outer Line + geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); + glEnable(GL_TEXTURE_2D); + } + glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; + + DependencyManager::get()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, + glm::vec2(magnifyULeft, magnifyVBottom), + glm::vec2(magnifyURight, magnifyVBottom), + glm::vec2(magnifyURight, magnifyVTop), + glm::vec2(magnifyULeft, magnifyVTop), + magnifierColor, _magnifierQuad); + + } glPopMatrix(); +} + +void ApplicationCompositor::buildHemiVertices( + const float fov, const float aspectRatio, const int slices, const int stacks) { + static float textureFOV = 0.0f, textureAspectRatio = 1.0f; + if (textureFOV == fov && textureAspectRatio == aspectRatio) { + return; + } + + textureFOV = fov; + textureAspectRatio = aspectRatio; + + auto geometryCache = DependencyManager::get(); + + _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); + _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); + + + if (fov >= PI) { + qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; + } + + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm + + vec3 pos; + vec2 uv; + // Compute vertices positions and texture UV coordinate + // Create and write to buffer + for (int i = 0; i < stacks; i++) { + uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + // abs(theta) <= fov / 2.0f + float pitch = -fov * (uv.y - 0.5f); + for (int j = 0; j < slices; j++) { + uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + // abs(phi) <= fov * aspectRatio / 2.0f + float yaw = -fov * aspectRatio * (uv.x - 0.5f); + pos = getPoint(yaw, pitch); + static const vec4 color(1); + _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); + _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); + _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); + } + } + + // Compute number of indices needed + static const int VERTEX_PER_TRANGLE = 3; + static const int TRIANGLE_PER_RECTANGLE = 2; + int numberOfRectangles = (slices - 1) * (stacks - 1); + _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + + // Compute indices order + std::vector indices; + for (int i = 0; i < stacks - 1; i++) { + for (int j = 0; j < slices - 1; j++) { + GLushort bottomLeftIndex = i * slices + j; + GLushort bottomRightIndex = bottomLeftIndex + 1; + GLushort topLeftIndex = bottomLeftIndex + slices; + GLushort topRightIndex = topLeftIndex + 1; + // FIXME make a z-order curve for better vertex cache locality + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); + + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); + } + } + _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); +} + + +void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) { + buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); + static const int VERTEX_DATA_SLOT = 0; + static const int TEXTURE_DATA_SLOT = 1; + static const int COLOR_DATA_SLOT = 2; + gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone + streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); + batch.setInputFormat(streamFormat); + + static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); + gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); + gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); + gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); + batch.setInputBuffer(VERTEX_DATA_SLOT, posView); + batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); + batch.setInputBuffer(COLOR_DATA_SLOT, colView); + batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); + batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); +} + +glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) { + glm::vec2 result; + // Compute yaw + glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); + result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); + if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { + result.x = -glm::abs(result.x); + } else { + result.x = glm::abs(result.x); + } + // Compute pitch + result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; + + return result; +} + +glm::vec3 ApplicationCompositor::sphericalToDirection(const glm::vec2& sphericalPos) { + glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); + return rotation * IDENTITY_FRONT; +} + +glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) { + auto screenSize = qApp->getCanvasSize(); + glm::vec2 result; + result.x = -(screenPos.x / screenSize.x - 0.5f); + result.y = (screenPos.y / screenSize.y - 0.5f); + result.x *= MOUSE_YAW_RANGE; + result.y *= MOUSE_PITCH_RANGE; + + return result; +} + +glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= MOUSE_RANGE; + result += 0.5f; + result *= qApp->getCanvasSize(); + return result; +} + +glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const { + glm::vec2 result = sphericalPos; + result.x *= -1.0; + result /= _textureFov; + result.x /= _textureAspectRatio; + result += 0.5f; + result *= qApp->getCanvasSize(); + return result; +} + +glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const { + glm::vec2 result = overlayPos; + result /= qApp->getCanvasSize(); + result -= 0.5f; + result *= _textureFov; + result.x *= _textureAspectRatio; + result.x *= -1.0f; + return result; +} + +glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const { + return sphericalToOverlay(screenToSpherical(screenPos)); +} + +glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const { + return sphericalToScreen(overlayToSpherical(overlayPos)); +} + +void ApplicationCompositor::updateTooltips() { + if (_hoverItemId != _noItemId) { + quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; + if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) { + // TODO Enable and position the tooltip + _hoverItemEnterUsecs = UINT64_MAX; + _tooltipId = Tooltip::showTip("URL: " + _hoverItemHref); + } + } +} diff --git a/interface/src/ui/ApplicationCompositor.h b/interface/src/ui/ApplicationCompositor.h new file mode 100644 index 0000000000..fc0b37127f --- /dev/null +++ b/interface/src/ui/ApplicationCompositor.h @@ -0,0 +1,120 @@ +// +// Created by Bradley Austin Davis Arnold on 2015/06/13 +// 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_ApplicationCompositor_h +#define hifi_ApplicationCompositor_h + +#include +#include + +#include +#include +#include +#include +#include + +class Camera; +class PalmData; +class RenderArgs; + +const float MAGNIFY_WIDTH = 220.0f; +const float MAGNIFY_HEIGHT = 100.0f; +const float MAGNIFY_MULT = 2.0f; + +const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; + +// Handles the drawing of the overlays to the screen +// TODO, move divide up the rendering, displaying and input handling +// facilities of this class +class ApplicationCompositor : public QObject { + Q_OBJECT +public: + ApplicationCompositor(); + ~ApplicationCompositor(); + + void displayOverlayTexture(RenderArgs* renderArgs); + void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye); + + QPoint getPalmClickLocation(const PalmData *palm) const; + bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; + + bool hasMagnifier() const { return _magnifier; } + void toggleMagnifier() { _magnifier = !_magnifier; } + + float getHmdUIAngularSize() const { return _hmdUIAngularSize; } + void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } + + // Converter from one frame of reference to another. + // Frame of reference: + // Direction: Ray that represents the spherical values + // Screen: Position on the screen (x,y) + // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) + // Overlay: Position on the overlay (x,y) + // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. + // This allows for picking outside of the screen projection in 3D. + glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; + glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; + glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; + glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; + void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; + GLuint getOverlayTexture() const; + + static glm::vec2 directionToSpherical(const glm::vec3 & direction); + static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); + static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); + static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); + +private: + void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); + void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); + void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); + void drawSphereSection(gpu::Batch& batch); + void updateTooltips(); + + void renderPointers(gpu::Batch& batch); + void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder); + void renderControllerPointers(gpu::Batch& batch); + void renderPointersOculus(gpu::Batch& batch); + + // Support for hovering and tooltips + static EntityItemID _noItemId; + EntityItemID _hoverItemId{ _noItemId }; + QString _hoverItemHref; + quint64 _hoverItemEnterUsecs{ 0 }; + + float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; + float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) }; + float _textureAspectRatio{ 1.0f }; + int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; + + enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; + bool _reticleActive[NUMBER_OF_RETICLES]; + QPoint _reticlePosition[NUMBER_OF_RETICLES]; + bool _magActive[NUMBER_OF_RETICLES]; + float _magSizeMult[NUMBER_OF_RETICLES]; + bool _magnifier{ true }; + + float _alpha{ 1.0f }; + float _oculusUIRadius{ 1.0f }; + + QMap _cursors; + + int _reticleQuad; + int _magnifierQuad; + int _magnifierBorder; + + int _previousBorderWidth{ -1 }; + int _previousBorderHeight{ -1 }; + + glm::vec3 _previousMagnifierBottomLeft; + glm::vec3 _previousMagnifierBottomRight; + glm::vec3 _previousMagnifierTopLeft; + glm::vec3 _previousMagnifierTopRight; +}; + +#endif // hifi_ApplicationCompositor_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2b58ad93fc..63f68b86ce 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -14,153 +14,45 @@ #include #include +#include + #include +#include #include -#include #include +#include #include -#include #include +#include +#include #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" #include "audio/AudioScope.h" -#include "audio/AudioToolBox.h" #include "Application.h" #include "ApplicationOverlay.h" -#include "devices/CameraToolBox.h" #include "Util.h" #include "ui/Stats.h" - -// Used to animate the magnification windows -const float MAG_SPEED = 0.08f; - -const quint64 MSECS_TO_USECS = 1000ULL; +#include "ui/AvatarInputs.h" const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; -const float reticleSize = TWO_PI / 100.0f; - - -const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; +const int AUDIO_METER_GAP = 5; +const int MUTE_ICON_PADDING = 10; +const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; +static const float ORTHO_NEAR_CLIP = -10000; +static const float ORTHO_FAR_CLIP = 10000; -static const float MOUSE_PITCH_RANGE = 1.0f * PI; -static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; -static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); - -// Return a point's cartesian coordinates on a sphere from pitch and yaw -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))); +// TODO move somewhere useful +static void fboViewport(QOpenGLFramebufferObject* fbo) { + auto size = fbo->size(); + glViewport(0, 0, size.width(), size.height()); } -//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should -//be multiplied by dir and added to origin to get the location of the collision -bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) +ApplicationOverlay::ApplicationOverlay() { - //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection - - //Compute A, B and C coefficients - float a = glm::dot(dir, dir); - float b = 2 * glm::dot(dir, origin); - float c = glm::dot(origin, origin) - (r * r); - - //Find discriminant - float disc = b * b - 4 * a * c; - - // if discriminant is negative there are no real roots, so return - // false as ray misses sphere - if (disc < 0) { - return false; - } - - // compute q as described above - float distSqrt = sqrtf(disc); - float q; - if (b < 0) { - q = (-b - distSqrt) / 2.0f; - } else { - q = (-b + distSqrt) / 2.0f; - } - - // compute t0 and t1 - float t0 = q / a; - float t1 = c / q; - - // make sure t0 is smaller than t1 - if (t0 > t1) { - // if t0 is bigger than t1 swap them around - float temp = t0; - t0 = t1; - t1 = temp; - } - - // if t1 is less than zero, the object is in the ray's negative direction - // and consequently the ray misses the sphere - if (t1 < 0) { - return false; - } - - // if t0 is less than zero, the intersection point is at t1 - if (t0 < 0) { - *result = t1; - return true; - } else { // else the intersection point is at t0 - *result = t0; - return true; - } -} - -void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { - glPushMatrix(); { - glm::vec3 axis = glm::axis(orientation); - glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z); - glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f); - glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f); - - // TODO: this version of renderQuad() needs to take a color - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; - - - - DependencyManager::get()->renderQuad(topLeft, bottomLeft, bottomRight, topRight, - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - } glPopMatrix(); -} - -ApplicationOverlay::ApplicationOverlay() : - _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), - _textureAspectRatio(1.0f), - _lastMouseMove(0), - _magnifier(true), - _alpha(1.0f), - _oculusUIRadius(1.0f), - _trailingAudioLoudness(0.0f), - _previousBorderWidth(-1), - _previousBorderHeight(-1), - _previousMagnifierBottomLeft(), - _previousMagnifierBottomRight(), - _previousMagnifierTopLeft(), - _previousMagnifierTopRight() -{ - memset(_reticleActive, 0, sizeof(_reticleActive)); - memset(_magActive, 0, sizeof(_reticleActive)); - memset(_magSizeMult, 0, sizeof(_magSizeMult)); - auto geometryCache = DependencyManager::get(); - - _reticleQuad = geometryCache->allocateID(); - _magnifierQuad = geometryCache->allocateID(); - _audioRedQuad = geometryCache->allocateID(); - _audioGreenQuad = geometryCache->allocateID(); - _audioBlueQuad = geometryCache->allocateID(); _domainStatusBorder = geometryCache->allocateID(); _magnifierBorder = geometryCache->allocateID(); @@ -173,7 +65,7 @@ ApplicationOverlay::ApplicationOverlay() : auto offscreenUi = DependencyManager::get(); offscreenUi->lockTexture(textureId); assert(!glGetError()); - std::swap(_newUiTexture, textureId); + std::swap(_uiTexture, textureId); if (textureId) { offscreenUi->releaseTexture(textureId); } @@ -185,845 +77,81 @@ ApplicationOverlay::~ApplicationOverlay() { // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { + CHECK_GL_ERROR(); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); - Overlays& overlays = qApp->getOverlays(); - _textureFov = glm::radians(_hmdUIAngularSize); - glm::vec2 size = qApp->getCanvasSize(); - _textureAspectRatio = aspect(size); + // TODO move to Application::idle()? + Stats::getInstance()->updateStats(); + AvatarInputs::getInstance()->update(); - //Handle fading and deactivation/activation of UI + buildFramebufferObject(); - // Render 2D overlay - glMatrixMode(GL_PROJECTION); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _overlays.buildFramebufferObject(); - _overlays.bind(); + // Execute the batch into our framebuffer + _overlayFramebuffer->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, size.x, size.y); + fboViewport(_overlayFramebuffer); - glPushMatrix(); { - const float NEAR_CLIP = -10000; - const float FAR_CLIP = 10000; - glLoadIdentity(); - glOrtho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); - - glMatrixMode(GL_MODELVIEW); - - renderAudioMeter(); - renderCameraToggle(); - - renderStatsAndLogs(); - - // give external parties a change to hook in - emit qApp->renderingOverlay(); - - overlays.renderHUD(renderArgs); - - renderPointers(); - - renderDomainConnectionStatusBorder(); - - glMatrixMode(GL_PROJECTION); - } glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - - _overlays.release(); + // Now render the overlay components together into a single texture + renderOverlays(renderArgs); + renderStatsAndLogs(renderArgs); + renderDomainConnectionStatusBorder(renderArgs); + renderQmlUi(renderArgs); + _overlayFramebuffer->release(); + CHECK_GL_ERROR(); } -// A quick and dirty solution for compositing the old overlay -// texture with the new one -template -void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - if (firstPassTexture) { - glBindTexture(GL_TEXTURE_2D, firstPassTexture); - f(); +void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { + if (_uiTexture) { + gpu::Batch batch; + auto geometryCache = DependencyManager::get(); + geometryCache->useSimpleDrawPipeline(batch); + batch.setProjectionTransform(mat4()); + batch.setModelTransform(mat4()); + batch._glBindTexture(GL_TEXTURE_2D, _uiTexture); + geometryCache->renderUnitQuad(batch, glm::vec4(1)); + renderArgs->_context->syncCache(); + renderArgs->_context->render(batch); } - if (secondPassTexture) { - glBindTexture(GL_TEXTURE_2D, secondPassTexture); - f(); - } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); } -// Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture() { - if (_alpha == 0.0f) { - return; - } +void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { + glm::vec2 size = qApp->getCanvasSize(); + + mat4 legacyProjection = glm::ortho(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); glMatrixMode(GL_PROJECTION); - glPushMatrix(); { - glLoadIdentity(); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); - - static const glm::vec2 topLeft(-1, 1); - static const glm::vec2 bottomRight(1, -1); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad( - topLeft, bottomRight, - texCoordTopLeft, texCoordBottomRight, - glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); - }); - - if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } - - //draw the mouse pointer - glm::vec2 canvasSize = qApp->getCanvasSize(); - glm::vec2 mouseSize = 32.0f / canvasSize; - auto mouseTopLeft = topLeft * mouseSize; - auto mouseBottomRight = bottomRight * mouseSize; - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; - - glEnable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderQuad( - mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, - texCoordTopLeft, texCoordBottomRight, - reticleColor); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glDisable(GL_TEXTURE_2D); - } glPopMatrix(); -} - -// Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { - if (_alpha == 0.0f) { - return; - } - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDisable(GL_LIGHTING); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.01f); - - - //Update and draw the magnifiers - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::quat& orientation = myAvatar->getOrientation(); - // Always display the HMD overlay relative to the camera position but - // remove the HMD pose offset. This results in an overlay that sticks with you - // even in third person mode, but isn't drawn at a fixed distance. - glm::vec3 position = whichCamera.getPosition(); - position -= qApp->getCamera()->getHmdPosition(); - const float scale = myAvatar->getScale() * _oculusUIRadius; - -// glm::vec3 eyeOffset = setEyeOffsetPosition; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); { - glTranslatef(position.x, position.y, position.z); - glm::mat4 rotation = glm::toMat4(orientation); - glMultMatrixf(&rotation[0][0]); - glScalef(scale, scale, scale); - for (int i = 0; i < NUMBER_OF_RETICLES; i++) { - - if (_magActive[i]) { - _magSizeMult[i] += MAG_SPEED; - if (_magSizeMult[i] > 1.0f) { - _magSizeMult[i] = 1.0f; - } - } else { - _magSizeMult[i] -= MAG_SPEED; - if (_magSizeMult[i] < 0.0f) { - _magSizeMult[i] = 0.0f; - } - } - - if (_magSizeMult[i] > 0.0f) { - //Render magnifier, but dont show border for mouse magnifier - glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - with_each_texture(_overlays.getTexture(), 0, [&] { - renderMagnifier(projection, _magSizeMult[i], i != MOUSE); - }); - } - } - - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); - - static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (textureFOV != _textureFov || - textureAspectRatio != _textureAspectRatio) { - textureFOV = _textureFov; - textureAspectRatio = _textureAspectRatio; - - _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); - } - - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - _overlays.render(); - }); - - if (!Application::getInstance()->isMouseHidden()) { - renderPointersOculus(); - } - glDepthMask(GL_TRUE); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); - } glPopMatrix(); -} - -// Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { - if (_alpha == 0.0f) { - return; - } - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation(); - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - // Transform to world space - glm::quat rotation = whichCamera.getRotation(); - glm::vec3 axis2 = glm::axis(rotation); - glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); - glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); - - // Translate to the front of the camera - glm::vec3 pos = whichCamera.getPosition(); - glm::quat rot = myAvatar->getOrientation(); - glm::vec3 axis = glm::axis(rot); - - glTranslatef(pos.x, pos.y, pos.z); - glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); - - glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha}; - - //Render - const GLfloat distance = 1.0f; - - const GLfloat halfQuadHeight = distance * tanf(fov); - const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; - const GLfloat quadWidth = halfQuadWidth * 2.0f; - const GLfloat quadHeight = halfQuadHeight * 2.0f; - - GLfloat x = -halfQuadWidth; - GLfloat y = -halfQuadHeight; - glDisable(GL_DEPTH_TEST); - - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y, -distance), - glm::vec3(x, y, -distance), - glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), - glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), - overlayColor); - }); - - if (!_crosshairTexture) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + - "images/sixense-reticle.png"); - } - - //draw the mouse pointer - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec2 canvasSize = qApp->getCanvasSize(); - const float reticleSize = 40.0f / canvasSize.x * quadWidth; - x -= reticleSize / 2.0f; - y += reticleSize / 2.0f; - const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; - const float mouseY = (1.0f - ((float)qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; - - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - - DependencyManager::get()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance), - glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance), - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - - glEnable(GL_DEPTH_TEST); - - glPopMatrix(); - - glDepthMask(GL_TRUE); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); -} - -void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { - cursorPos *= qApp->getCanvasSize(); - const glm::vec2 projection = screenToSpherical(cursorPos); - // The overlay space orientation of the mouse coordinates - const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. - // Now we need to account for the actual camera position relative to the overlay - glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); - - - const glm::vec3& hmdPosition = qApp->getCamera()->getHmdPosition(); - const glm::quat& hmdOrientation = qApp->getCamera()->getHmdRotation(); - - // We need the RAW camera orientation and position, because this is what the overlay is - // rendered relative to - const glm::vec3 overlayPosition = qApp->getCamera()->getPosition() - hmdPosition; - const glm::quat overlayOrientation = qApp->getCamera()->getRotation() * glm::inverse(hmdOrientation); - - // Intersection UI overlay space - glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; - glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius; - intersectionWithUi += overlayPosition; - - // Intersection in world space - origin = overlayPosition + hmdPosition; - direction = glm::normalize(intersectionWithUi - origin); -} - -glm::vec2 getPolarCoordinates(const PalmData& palm) { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - auto avatarOrientation = myAvatar->getOrientation(); - auto eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - // Direction of the tip relative to the eye - glm::vec3 tipDirection = tip - eyePos; - // orient into avatar space - tipDirection = glm::inverse(avatarOrientation) * tipDirection; - // Normalize for trig functions - tipDirection = glm::normalize(tipDirection); - // Convert to polar coordinates - glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); - return polar; -} - -//Caculate the click location using one of the sixense controllers. Scale is not applied -QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { - QPoint rv; - auto canvasSize = qApp->getCanvasSize(); - if (qApp->isHMDMode()) { - glm::vec2 polar = getPolarCoordinates(*palm); - glm::vec2 point = sphericalToScreen(-polar); - rv.rx() = point.x; - rv.ry() = point.y; - } else { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - glm::dmat4 projection; - qApp->getProjectionMatrix(&projection); - glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); - glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); - glm::vec3 tipPos = invOrientation * (tip - eyePos); - - glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); - glm::vec3 ndcSpacePos; - if (clipSpacePos.w != 0) { - ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; - } - - rv.setX(((ndcSpacePos.x + 1.0f) / 2.0f) * canvasSize.x); - rv.setY((1.0f - ((ndcSpacePos.y + 1.0f) / 2.0f)) * canvasSize.y); - } - return rv; -} - -//Finds the collision point of a world space ray -bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - - glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); - - glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); - glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); - - float t; - if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ - result = position + direction * t; - return true; - } - - return false; -} - -//Renders optional pointers -void ApplicationOverlay::renderPointers() { - //lazily load crosshair texture - if (_crosshairTexture == 0) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - //glActiveTexture(GL_TEXTURE0); - //bindCursorTexture(); - - if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { - //If we are in oculus, render reticle later - if (_lastMouseMove == 0) { - _lastMouseMove = usecTimestampNow(); - } - QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); - - static const int MAX_IDLE_TIME = 3; - if (_reticlePosition[MOUSE] != position) { - _lastMouseMove = usecTimestampNow(); - } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { - //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians - glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); - glm::vec3 result; - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - if (calculateRayUICollisionPoint(myAvatar->getEyePosition(), - myAvatar->getOrientation() * orientation * IDENTITY_FRONT, - result)) { - glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition()); - glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); - glm::vec2 screenPos = sphericalToScreen(spericalPos); - position = QPoint(screenPos.x, screenPos.y); - // FIXME - //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); - } else { - qDebug() << "No collision point"; - } - } - - _reticlePosition[MOUSE] = position; - _reticleActive[MOUSE] = true; - _magActive[MOUSE] = _magnifier; - _reticleActive[LEFT_CONTROLLER] = false; - _reticleActive[RIGHT_CONTROLLER] = false; - } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - _lastMouseMove = 0; - //only render controller pointer if we aren't already rendering a mouse pointer - _reticleActive[MOUSE] = false; - _magActive[MOUSE] = false; - renderControllerPointers(); - } - //glBindTexture(GL_TEXTURE_2D, 0); - //glDisable(GL_TEXTURE_2D); -} - -void ApplicationOverlay::renderControllerPointers() { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - - //Static variables used for storing controller state - static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; - static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; - static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; - - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - - for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { - const int index = palmIndex - 1; - - const PalmData* palmData = NULL; - - if (palmIndex >= handData->getPalms().size()) { - return; - } - - if (handData->getPalms()[palmIndex].isActive()) { - palmData = &handData->getPalms()[palmIndex]; - } else { - continue; - } - - int controllerButtons = palmData->getControllerButtons(); - - //Check for if we should toggle or drag the magnification window - if (controllerButtons & BUTTON_3) { - if (isPressed[index] == false) { - //We are now dragging the window - isPressed[index] = true; - //set the pressed time in us - pressedTime[index] = usecTimestampNow(); - stateWhenPressed[index] = _magActive[index]; - } - } else if (isPressed[index]) { - isPressed[index] = false; - //If the button was only pressed for < 250 ms - //then disable it. - - const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; - if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { - _magActive[index] = !stateWhenPressed[index]; - } - } - - //if we have the oculus, we should make the cursor smaller since it will be - //magnified - if (qApp->isHMDMode()) { - - QPoint point = getPalmClickLocation(palmData); - - _reticlePosition[index] = point; - - //When button 2 is pressed we drag the mag window - if (isPressed[index]) { - _magActive[index] = true; - } - - // If oculus is enabled, we draw the crosshairs later - continue; - } - - auto canvasSize = qApp->getCanvasSize(); - int mouseX, mouseY; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { - QPoint res = getPalmClickLocation(palmData); - mouseX = res.x(); - mouseY = res.y(); - } else { - // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - - // Get the angles, scaled between (-0.5,0.5) - float xAngle = (atan2(direction.z, direction.x) + M_PI_2); - float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); - - // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); - - mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); - mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); - } - - //If the cursor is out of the screen then don't render it - if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { - _reticleActive[index] = false; - continue; - } - _reticleActive[index] = true; - - - const float reticleSize = 40.0f; - - mouseX -= reticleSize / 2.0f; - mouseY += reticleSize / 2.0f; - - - glm::vec2 topLeft(mouseX, mouseY); - glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); - glm::vec2 texCoordTopLeft(0.0f, 0.0f); - glm::vec2 texCoordBottomRight(1.0f, 1.0f); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); - - } -} - -void ApplicationOverlay::renderPointersOculus() { - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glDisable(GL_DEPTH_TEST); - + glLoadMatrixf(glm::value_ptr(legacyProjection)); glMatrixMode(GL_MODELVIEW); - //Controller Pointers - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); - // Render reticle at location - renderReticle(orientation, _alpha); - } - } + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - renderReticle(orientation, _alpha); - } + // give external parties a change to hook in + emit qApp->renderingOverlay(); + qApp->getOverlays().renderHUD(renderArgs); - glEnable(GL_DEPTH_TEST); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + renderArgs->_context->syncCache(); + fboViewport(_overlayFramebuffer); } -//Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { - if (!_magnifier) { - return; - } - auto canvasSize = qApp->getCanvasSize(); - - const int widgetWidth = canvasSize.x; - const int widgetHeight = canvasSize.y; - - const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; - const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; - // Magnification Texture Coordinates - const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; - const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; - const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; - const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; - - const float newHalfWidth = halfWidth * MAGNIFY_MULT; - const float newHalfHeight = halfHeight * MAGNIFY_MULT; - //Get yaw / pitch value for the corners - const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, - magPos.y - newHalfHeight)); - const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, - magPos.y + newHalfHeight)); - - const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); - const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); - const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); - const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); - - auto geometryCache = DependencyManager::get(); - - if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight - || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { - QVector border; - border << topLeft; - border << bottomLeft; - border << bottomRight; - border << topRight; - border << topLeft; - geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); - - _previousMagnifierBottomLeft = bottomLeft; - _previousMagnifierBottomRight = bottomRight; - _previousMagnifierTopLeft = topLeft; - _previousMagnifierTopRight = topRight; - } - - glPushMatrix(); { - if (showBorder) { - glDisable(GL_TEXTURE_2D); - glLineWidth(1.0f); - //Outer Line - geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); - glEnable(GL_TEXTURE_2D); - } - glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; - - DependencyManager::get()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, - glm::vec2(magnifyULeft, magnifyVBottom), - glm::vec2(magnifyURight, magnifyVBottom), - glm::vec2(magnifyURight, magnifyVTop), - glm::vec2(magnifyULeft, magnifyVTop), - magnifierColor, _magnifierQuad); - - } glPopMatrix(); +void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { } -const int AUDIO_METER_GAP = 5; -const int MUTE_ICON_PADDING = 10; - -void ApplicationOverlay::renderCameraToggle() { - if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { - return; - } - - int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); - bool boxed = smallMirrorVisible && - !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - if (boxed) { - audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; - } else { - audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; - } - - DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); +void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { } -void ApplicationOverlay::renderAudioMeter() { - auto audio = DependencyManager::get(); - - // Audio VU Meter and Mute Icon - const int MUTE_ICON_SIZE = 24; - const int AUDIO_METER_HEIGHT = 8; - const int INTER_ICON_GAP = 2; - - int cameraSpace = 0; - int audioMeterWidth = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING; - int audioMeterScaleWidth = audioMeterWidth - 2; - int audioMeterX = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; - if (!Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { - cameraSpace = MUTE_ICON_SIZE + INTER_ICON_GAP; - audioMeterWidth -= cameraSpace; - audioMeterScaleWidth -= cameraSpace; - audioMeterX += cameraSpace; - } - - int audioMeterY; - bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); - bool boxed = smallMirrorVisible && - !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); - if (boxed) { - audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; - } else { - audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; - } - - const glm::vec4 AUDIO_METER_BLUE = { 0.0, 0.0, 1.0, 1.0 }; - const glm::vec4 AUDIO_METER_GREEN = { 0.0, 1.0, 0.0, 1.0 }; - const glm::vec4 AUDIO_METER_RED = { 1.0, 0.0, 0.0, 1.0 }; - const float CLIPPING_INDICATOR_TIME = 1.0f; - const float AUDIO_METER_AVERAGING = 0.5; - const float LOG2 = log(2.0f); - const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; - const float LOG2_LOUDNESS_FLOOR = 11.0f; - float audioGreenStart = 0.25f * audioMeterScaleWidth; - float audioRedStart = 0.8f * audioMeterScaleWidth; - float audioLevel = 0.0f; - float loudness = audio->getLastInputLoudness() + 1.0f; - - _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; - float log2loudness = logf(_trailingAudioLoudness) / LOG2; - - if (log2loudness <= LOG2_LOUDNESS_FLOOR) { - audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; - } else { - audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; - } - if (audioLevel > audioMeterScaleWidth) { - audioLevel = audioMeterScaleWidth; - } - bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); - - DependencyManager::get()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed); - - auto canvasSize = qApp->getCanvasSize(); - DependencyManager::get()->render(canvasSize.x, canvasSize.y); - DependencyManager::get()->render(WHITE_TEXT, canvasSize.x, canvasSize.y); - - audioMeterY += AUDIO_METER_HEIGHT; - - // Draw audio meter background Quad - DependencyManager::get()->renderQuad(audioMeterX, audioMeterY, audioMeterWidth, AUDIO_METER_HEIGHT, - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - - if (audioLevel > audioRedStart) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_RED; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Red Quad - DependencyManager::get()->renderQuad(audioMeterX + audioRedStart, - audioMeterY, - audioLevel - audioRedStart, - AUDIO_METER_HEIGHT, quadColor, - _audioRedQuad); - - audioLevel = audioRedStart; - } - - if (audioLevel > audioGreenStart) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_GREEN; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Green Quad - DependencyManager::get()->renderQuad(audioMeterX + audioGreenStart, - audioMeterY, - audioLevel - audioGreenStart, - AUDIO_METER_HEIGHT, quadColor, - _audioGreenQuad); - - audioLevel = audioGreenStart; - } - - if (audioLevel >= 0) { - glm::vec4 quadColor; - if (!isClipping) { - quadColor = AUDIO_METER_BLUE; - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - // Draw Blue (low level) quad - DependencyManager::get()->renderQuad(audioMeterX, - audioMeterY, - audioLevel, AUDIO_METER_HEIGHT, quadColor, - _audioBlueQuad); - } -} - -void ApplicationOverlay::renderStatsAndLogs() { - Application* application = Application::getInstance(); - QSharedPointer bandwidthRecorder = DependencyManager::get(); - - const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); - NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); - +void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { // Display stats and log text onscreen - glLineWidth(1.0f); - glPointSize(1.0f); // Determine whether to compute timing details - bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && - Menu::getInstance()->isOptionChecked(MenuOption::Stats) && - Stats::getInstance()->isExpanded(); - if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { - PerformanceTimer::setActive(shouldDisplayTimingDetail); - } - - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - // let's set horizontal offset to give stats some margin to mirror - int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; - int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); - // Onscreen text about position, servers, etc - Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), - bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(), - bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(), - bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(), - bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(), - voxelPacketsToProcess); - } + /* // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { auto canvasSize = qApp->getCanvasSize(); @@ -1035,159 +163,77 @@ void ApplicationOverlay::renderStatsAndLogs() { drawText(canvasSize.x - 100, canvasSize.y - timerBottom, 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); } + + glPointSize(1.0f); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + NodeBounds& nodeBoundsDisplay = qApp->getNodeBoundsDisplay(); nodeBoundsDisplay.drawOverlay(); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + fboViewport(_overlayFramebuffer); + */ } -void ApplicationOverlay::renderDomainConnectionStatusBorder() { +void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { + auto geometryCache = DependencyManager::get(); + static std::once_flag once; + std::call_once(once, [&] { + QVector points; + static const float B = 0.99f; + points.push_back(vec2(-B)); + points.push_back(vec2(B, -B)); + points.push_back(vec2(B)); + points.push_back(vec2(-B, B)); + points.push_back(vec2(-B)); + geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR); + }); auto nodeList = DependencyManager::get(); - if (nodeList && !nodeList->getDomainHandler().isConnected()) { + gpu::Batch batch; auto geometryCache = DependencyManager::get(); - auto canvasSize = qApp->getCanvasSize(); - if ((int)canvasSize.x != _previousBorderWidth || (int)canvasSize.y != _previousBorderHeight) { - glm::vec4 color(CONNECTION_STATUS_BORDER_COLOR[0], - CONNECTION_STATUS_BORDER_COLOR[1], - CONNECTION_STATUS_BORDER_COLOR[2], 1.0f); + geometryCache->useSimpleDrawPipeline(batch); + batch.setProjectionTransform(mat4()); + batch.setModelTransform(mat4()); + batch.setUniformTexture(0, DependencyManager::get()->getWhiteTexture()); + batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); - QVector border; - border << glm::vec2(0, 0); - border << glm::vec2(0, canvasSize.y); - border << glm::vec2(canvasSize.x, canvasSize.y); - border << glm::vec2(canvasSize.x, 0); - border << glm::vec2(0, 0); - geometryCache->updateVertices(_domainStatusBorder, border, color); - _previousBorderWidth = canvasSize.x; - _previousBorderHeight = canvasSize.y; - } + // TODO animate the disconnect border for some excitement while not connected? + //double usecs = usecTimestampNow(); + //double secs = usecs / 1000000.0; + //float scaleAmount = 1.0f + (0.01f * sin(secs * 5.0f)); + //batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount))); - glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); - - geometryCache->renderVertices(gpu::LINE_STRIP, _domainStatusBorder); + geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder); + renderArgs->_context->syncCache(); + renderArgs->_context->render(batch); } } -ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : - _vertices(0), - _indices(0), - _framebufferObject(NULL), - _vbo(0, 0) { +GLuint ApplicationOverlay::getOverlayTexture() { + if (!_overlayFramebuffer) { + return 0; + } + return _overlayFramebuffer->texture(); } -ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { - cleanupVBO(); - if (_framebufferObject != NULL) { - delete _framebufferObject; - } -} - -void ApplicationOverlay::TexturedHemisphere::bind() { - _framebufferObject->bind(); -} - -void ApplicationOverlay::TexturedHemisphere::release() { - _framebufferObject->release(); -} - -void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov, - const float aspectRatio, - const int slices, - const int stacks) { - if (fov >= PI) { - qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; - } - // Cleanup old VBO if necessary - cleanupVBO(); - - //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - - // Compute number of vertices needed - _vertices = slices * stacks; - - // Compute vertices positions and texture UV coordinate - TextureVertex* vertexData = new TextureVertex[_vertices]; - TextureVertex* vertexPtr = &vertexData[0]; - for (int i = 0; i < stacks; i++) { - float stacksRatio = (float)i / (float)(stacks - 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 < slices; j++) { - float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f - // abs(phi) <= fov * aspectRatio / 2.0f - float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); - - vertexPtr->position = getPoint(yaw, pitch); - vertexPtr->uv.x = slicesRatio; - vertexPtr->uv.y = stacksRatio; - vertexPtr++; - } - } - // Create and write to buffer - glGenBuffers(1, &_vbo.first); - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - static const int BYTES_PER_VERTEX = sizeof(TextureVertex); - glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); - delete[] vertexData; - - - // Compute number of indices needed - static const int VERTEX_PER_TRANGLE = 3; - static const int TRIANGLE_PER_RECTANGLE = 2; - int numberOfRectangles = (slices - 1) * (stacks - 1); - _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; - - // Compute indices order - GLushort* indexData = new GLushort[_indices]; - GLushort* indexPtr = indexData; - for (int i = 0; i < stacks - 1; i++) { - for (int j = 0; j < slices - 1; j++) { - GLushort bottomLeftIndex = i * slices + j; - GLushort bottomRightIndex = bottomLeftIndex + 1; - GLushort topLeftIndex = bottomLeftIndex + slices; - GLushort topRightIndex = topLeftIndex + 1; - - *(indexPtr++) = topLeftIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = topRightIndex; - - *(indexPtr++) = topRightIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = bottomRightIndex; - } - } - // Create and write to buffer - glGenBuffers(1, &_vbo.second); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - static const int BYTES_PER_INDEX = sizeof(GLushort); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW); - delete[] indexData; -} - -void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { - if (_vbo.first != 0) { - glDeleteBuffers(1, &_vbo.first); - _vbo.first = 0; - } - if (_vbo.second != 0) { - glDeleteBuffers(1, &_vbo.second); - _vbo.second = 0; - } -} - -void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { - auto canvasSize = qApp->getCanvasSize(); - QSize fboSize = QSize(canvasSize.x, canvasSize.y); - if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { - // Already build +void ApplicationOverlay::buildFramebufferObject() { + QSize fboSize = qApp->getDeviceSize(); + if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) { + // Already built return; } - - if (_framebufferObject != NULL) { - delete _framebufferObject; + + if (_overlayFramebuffer) { + delete _overlayFramebuffer; } - - _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); - glBindTexture(GL_TEXTURE_2D, getTexture()); + + _overlayFramebuffer = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); + glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -1196,104 +242,3 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); glBindTexture(GL_TEXTURE_2D, 0); } - -//Renders a hemisphere with texture coordinates. -void ApplicationOverlay::TexturedHemisphere::render() { - if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) { - qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; - return; - } - - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - static const int STRIDE = sizeof(TextureVertex); - static const void* VERTEX_POINTER = 0; - static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3); - glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER); - glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER); - - glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { - return _framebufferObject->texture(); -} - -glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { - glm::vec2 result; - // Compute yaw - glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); - result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); - if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { - result.x = -glm::abs(result.x); - } else { - result.x = glm::abs(result.x); - } - // Compute pitch - result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; - - return result; -} - -glm::vec3 ApplicationOverlay::sphericalToDirection(const glm::vec2& sphericalPos) { - glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); - return rotation * IDENTITY_FRONT; -} - -glm::vec2 ApplicationOverlay::screenToSpherical(const glm::vec2& screenPos) { - auto screenSize = qApp->getCanvasSize(); - glm::vec2 result; - result.x = -(screenPos.x / screenSize.x - 0.5f); - result.y = (screenPos.y / screenSize.y - 0.5f); - result.x *= MOUSE_YAW_RANGE; - result.y *= MOUSE_PITCH_RANGE; - - return result; -} - -glm::vec2 ApplicationOverlay::sphericalToScreen(const glm::vec2& sphericalPos) { - glm::vec2 result = sphericalPos; - result.x *= -1.0f; - result /= MOUSE_RANGE; - result += 0.5f; - result *= qApp->getCanvasSize(); - return result; -} - -glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) const { - glm::vec2 result = sphericalPos; - result.x *= -1.0f; - result /= _textureFov; - result.x /= _textureAspectRatio; - result += 0.5f; - result *= qApp->getCanvasSize(); - return result; -} - -glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const { - glm::vec2 result = overlayPos; - result /= qApp->getCanvasSize(); - result -= 0.5f; - result *= _textureFov; - result.x *= _textureAspectRatio; - result.x *= -1.0f; - return result; -} - -glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const { - return sphericalToOverlay(screenToSpherical(screenPos)); -} - -glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const { - return sphericalToScreen(overlayToSpherical(overlayPos)); -} diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index eb397fe3c6..2f434ed7e1 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -13,16 +13,8 @@ #define hifi_ApplicationOverlay_h #include -class Camera; -class Overlays; class QOpenGLFramebufferObject; -const float MAGNIFY_WIDTH = 220.0f; -const float MAGNIFY_HEIGHT = 100.0f; -const float MAGNIFY_MULT = 2.0f; - -const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; - // Handles the drawing of the overlays to the screen // TODO, move divide up the rendering, displaying and input handling // facilities of this class @@ -33,122 +25,26 @@ public: ~ApplicationOverlay(); void renderOverlay(RenderArgs* renderArgs); - void displayOverlayTexture(); - void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); - void displayOverlayTextureHmd(Camera& whichCamera); + GLuint getOverlayTexture(); - QPoint getPalmClickLocation(const PalmData *palm) const; - bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; - - bool hasMagnifier() const { return _magnifier; } - void toggleMagnifier() { _magnifier = !_magnifier; } - - float getHmdUIAngularSize() const { return _hmdUIAngularSize; } - void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } - - // Converter from one frame of reference to another. - // Frame of reference: - // Direction: Ray that represents the spherical values - // Screen: Position on the screen (x,y) - // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) - // Overlay: Position on the overlay (x,y) - // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. - // This allows for picking outside of the screen projection in 3D. - glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; - glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; - glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; - glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; - void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; - - static glm::vec2 directionToSpherical(const glm::vec3 & direction); - static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); - static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); - static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); - private: - // Interleaved vertex data - struct TextureVertex { - glm::vec3 position; - glm::vec2 uv; - }; + void renderStatsAndLogs(RenderArgs* renderArgs); + void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); + void renderRearViewToFbo(RenderArgs* renderArgs); + void renderRearView(RenderArgs* renderArgs); + void renderQmlUi(RenderArgs* renderArgs); + void renderOverlays(RenderArgs* renderArgs); + void buildFramebufferObject(); - typedef QPair VerticesIndices; - class TexturedHemisphere { - public: - TexturedHemisphere(); - ~TexturedHemisphere(); - - void bind(); - void release(); - GLuint getTexture(); - - void buildFramebufferObject(); - void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); - void render(); - - private: - void cleanupVBO(); - - GLuint _vertices; - GLuint _indices; - QOpenGLFramebufferObject* _framebufferObject; - VerticesIndices _vbo; - }; - - float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - void renderReticle(glm::quat orientation, float alpha); - void renderPointers();; - void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); - - void renderControllerPointers(); - void renderPointersOculus(); - - void renderAudioMeter(); - void renderCameraToggle(); - void renderStatsAndLogs(); - void renderDomainConnectionStatusBorder(); - void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); + float _alpha{ 1.0f }; + float _trailingAudioLoudness{ 0.0f }; + GLuint _uiTexture{ 0 }; - TexturedHemisphere _overlays; - - float _textureFov; - float _textureAspectRatio; - - enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; - bool _reticleActive[NUMBER_OF_RETICLES]; - QPoint _reticlePosition[NUMBER_OF_RETICLES]; - bool _magActive[NUMBER_OF_RETICLES]; - float _magSizeMult[NUMBER_OF_RETICLES]; - quint64 _lastMouseMove; - bool _magnifier; - - float _alpha = 1.0f; - float _oculusUIRadius; - float _trailingAudioLoudness; - - gpu::TexturePointer _crosshairTexture; - - - QMap _cursors; - - GLuint _newUiTexture{ 0 }; - - int _reticleQuad; - int _magnifierQuad; - int _audioRedQuad; - int _audioGreenQuad; - int _audioBlueQuad; int _domainStatusBorder; int _magnifierBorder; - int _previousBorderWidth; - int _previousBorderHeight; - - glm::vec3 _previousMagnifierBottomLeft; - glm::vec3 _previousMagnifierBottomRight; - glm::vec3 _previousMagnifierTopLeft; - glm::vec3 _previousMagnifierTopRight; - + ivec2 _previousBorderSize{ -1 }; + QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr }; }; #endif // hifi_ApplicationOverlay_h diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp new file mode 100644 index 0000000000..11f744aaca --- /dev/null +++ b/interface/src/ui/AvatarInputs.cpp @@ -0,0 +1,134 @@ +// +// Created by Bradley Austin Davis 2015/06/19 +// Copyright 2013 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 "Application.h" +#include "AvatarInputs.h" + +#include + +#include "Menu.h" +#include "devices/FaceTracker.h" + +HIFI_QML_DEF(AvatarInputs) + + +static AvatarInputs* INSTANCE{ nullptr }; +static const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; +static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; + +static Setting::Handle rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0); + +AvatarInputs* AvatarInputs::getInstance() { + if (!INSTANCE) { + AvatarInputs::registerType(); + AvatarInputs::show(); + Q_ASSERT(INSTANCE); + } + return INSTANCE; +} + +AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { + INSTANCE = this; + int zoomSetting = rearViewZoomLevel.get(); + _mirrorZoomed = zoomSetting == 0; +} + +#define AI_UPDATE(name, src) \ + { \ + auto val = src; \ + if (_##name != val) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +#define AI_UPDATE_FLOAT(name, src, epsilon) \ + { \ + float val = src; \ + if (fabs(_##name - val) >= epsilon) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +void AvatarInputs::update() { + if (!Menu::getInstance()) { + return; + } + AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode() + && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); + AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); + AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); + + auto audioIO = DependencyManager::get(); + const float CLIPPING_INDICATOR_TIME = 1.0f; + const float AUDIO_METER_AVERAGING = 0.5; + const float LOG2 = log(2.0f); + const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; + const float LOG2_LOUDNESS_FLOOR = 11.0f; + float audioLevel = 0.0f; + auto audio = DependencyManager::get(); + float loudness = audio->getLastInputLoudness() + 1.0f; + + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; + + float log2loudness = logf(_trailingAudioLoudness) / LOG2; + + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { + audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE; + } else { + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE; + } + if (audioLevel > 1.0) { + audioLevel = 1.0; + } + AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01); + AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f))); + AI_UPDATE(audioMuted, audioIO->isMuted()); + + //// Make muted icon pulsate + //static const float PULSE_MIN = 0.4f; + //static const float PULSE_MAX = 1.0f; + //static const float PULSE_FREQUENCY = 1.0f; // in Hz + //qint64 now = usecTimestampNow(); + //if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { + // // Prevents t from getting too big, which would diminish glm::cos precision + // _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); + //} + //float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; + //float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; + //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; +} + +void AvatarInputs::toggleCameraMute() { + FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); + if (faceTracker) { + faceTracker->toggleMute(); + } +} + +void AvatarInputs::toggleAudioMute() { + DependencyManager::get()->toggleMute(); +} + +void AvatarInputs::resetSensors() { + qApp->resetSensors(); +} + +void AvatarInputs::toggleZoom() { + _mirrorZoomed = !_mirrorZoomed; + rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1); + emit mirrorZoomedChanged(); +} + +void AvatarInputs::closeMirror() { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + Menu::getInstance()->triggerOption(MenuOption::Mirror); + } +} diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h new file mode 100644 index 0000000000..8ed4e8f163 --- /dev/null +++ b/interface/src/ui/AvatarInputs.h @@ -0,0 +1,59 @@ +// +// Created by Bradley Austin Davis 2015/06/19 +// Copyright 2013 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_AvatarInputs_h +#define hifi_AvatarInputs_h + +#include +#include + +#define AI_PROPERTY(type, name, initialValue) \ + Q_PROPERTY(type name READ name NOTIFY name##Changed) \ +public: \ + type name() { return _##name; }; \ +private: \ + type _##name{ initialValue }; + +class AvatarInputs : public QQuickItem { + Q_OBJECT + HIFI_QML_DECL + + AI_PROPERTY(bool, cameraEnabled, false) + AI_PROPERTY(bool, cameraMuted, false) + AI_PROPERTY(bool, audioMuted, false) + AI_PROPERTY(bool, audioClipping, false) + AI_PROPERTY(float, audioLevel, 0) + AI_PROPERTY(bool, mirrorVisible, false) + AI_PROPERTY(bool, mirrorZoomed, true) + +public: + static AvatarInputs* getInstance(); + AvatarInputs(QQuickItem* parent = nullptr); + void update(); + +signals: + void cameraEnabledChanged(); + void cameraMutedChanged(); + void audioMutedChanged(); + void audioClippingChanged(); + void audioLevelChanged(); + void mirrorVisibleChanged(); + void mirrorZoomedChanged(); + +protected: + Q_INVOKABLE void resetSensors(); + Q_INVOKABLE void toggleCameraMute(); + Q_INVOKABLE void toggleAudioMute(); + Q_INVOKABLE void toggleZoom(); + Q_INVOKABLE void closeMirror(); + +private: + float _trailingAudioLoudness{ 0 }; +}; + +#endif // hifi_AvatarInputs_h diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index febd6f90bf..eca250a428 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -175,7 +175,7 @@ void PreferencesDialog::loadPreferences() { ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond()); - ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize()); + ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); SixenseManager& sixense = SixenseManager::getInstance(); ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); @@ -239,7 +239,7 @@ void PreferencesDialog::savePreferences() { qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value()); - qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); SixenseManager& sixense = SixenseManager::getInstance(); sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp deleted file mode 100644 index 33f3e1b487..0000000000 --- a/interface/src/ui/RearMirrorTools.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// -// RearMirrorTools.cpp -// interface/src/ui -// -// Created by Stojce Slavkovski on 10/23/2013. -// Copyright 2013 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 "InterfaceConfig.h" - -#include - -#include -#include -#include - -#include "Application.h" -#include "RearMirrorTools.h" -#include "Util.h" - -const int ICON_SIZE = 24; -const int ICON_PADDING = 5; - -const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; -const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; -Setting::Handle RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, - ZoomLevel::HEAD); - -RearMirrorTools::RearMirrorTools(QRect& bounds) : - _bounds(bounds), - _windowed(false), - _fullScreen(false) -{ - _closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); - - _zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); - _zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); - - _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE); - _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); - _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); - _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); - _headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); -} - -void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) { - if (fullScreen) { - _fullScreen = true; - displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture); - } else { - // render rear view tools if mouse is in the bounds - _windowed = _bounds.contains(mousePosition); - if (_windowed) { - displayIcon(_bounds, _closeIconRect, _closeTexture); - - ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get(); - displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD); - displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY); - } - } -} - -bool RearMirrorTools::mousePressEvent(int x, int y) { - if (_windowed) { - if (_closeIconRect.contains(x, y)) { - _windowed = false; - emit closeView(); - return true; - } - - if (_headZoomIconRect.contains(x, y)) { - rearViewZoomLevel.set(HEAD); - return true; - } - - if (_bodyZoomIconRect.contains(x, y)) { - rearViewZoomLevel.set(BODY); - return true; - } - - if (_bounds.contains(x, y)) { - _windowed = false; - emit restoreView(); - return true; - } - } - - if (_fullScreen) { - if (_shrinkIconRect.contains(x, y)) { - _fullScreen = false; - emit shrinkView(); - return true; - } - } - return false; -} - -void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) { - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - - glm::vec4 quadColor; - if (selected) { - quadColor = glm::vec4(.5f, .5f, .5f, 1.0f); - } else { - quadColor = glm::vec4(1, 1, 1, 1); - } - - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture)); - - glm::vec2 topLeft(iconBounds.left(), iconBounds.top()); - glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom()); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); - - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} diff --git a/interface/src/ui/RearMirrorTools.h b/interface/src/ui/RearMirrorTools.h deleted file mode 100644 index c633d72a49..0000000000 --- a/interface/src/ui/RearMirrorTools.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// RearMirrorTools.h -// interface/src/ui -// -// Created by Stojce Slavkovski on 10/23/2013. -// Copyright 2013 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_RearMirrorTools_h -#define hifi_RearMirrorTools_h - -#include -#include - -enum ZoomLevel { - HEAD = 0, - BODY = 1 -}; - -class RearMirrorTools : public QObject { - Q_OBJECT -public: - RearMirrorTools(QRect& bounds); - void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos); - bool mousePressEvent(int x, int y); - - static Setting::Handle rearViewZoomLevel; - -signals: - void closeView(); - void shrinkView(); - void resetView(); - void restoreView(); - -private: - QRect _bounds; - gpu::TexturePointer _closeTexture; - gpu::TexturePointer _zoomBodyTexture; - gpu::TexturePointer _zoomHeadTexture; - - QRect _closeIconRect; - QRect _resetIconRect; - QRect _shrinkIconRect; - QRect _headZoomIconRect; - QRect _bodyZoomIconRect; - - bool _windowed; - bool _fullScreen; - - void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false); -}; - -#endif // hifi_RearMirrorTools_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8359480b03..d6c61a33bd 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -1,17 +1,17 @@ // -// Stats.cpp -// interface/src/ui -// -// Created by Lucas Crisman on 22/03/14. +// Created by Bradley Austin Davis 2015/06/17 // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include -#include +#include "Stats.h" + +#include +#include #include #include @@ -25,147 +25,31 @@ #include #include -#include "Stats.h" #include "BandwidthRecorder.h" #include "InterfaceConfig.h" #include "Menu.h" #include "Util.h" #include "SequenceNumberStats.h" +HIFI_QML_DEF(Stats) + using namespace std; -const int STATS_PELS_PER_LINE = 16; -const int STATS_PELS_INITIALOFFSET = 12; - -const int STATS_GENERAL_MIN_WIDTH = 165; -const int STATS_PING_MIN_WIDTH = 190; -const int STATS_GEO_MIN_WIDTH = 240; -const int STATS_OCTREE_MIN_WIDTH = 410; +static Stats* INSTANCE{ nullptr }; Stats* Stats::getInstance() { - static Stats stats; - return &stats; + if (!INSTANCE) { + Stats::registerType(); + Stats::show(); + Q_ASSERT(INSTANCE); + } + return INSTANCE; } -Stats::Stats(): - _expanded(false), - _recentMaxPackets(0), - _resetRecentMaxPacketsSoon(true), - _generalStatsWidth(STATS_GENERAL_MIN_WIDTH), - _pingStatsWidth(STATS_PING_MIN_WIDTH), - _geoStatsWidth(STATS_GEO_MIN_WIDTH), - _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), - _lastHorizontalOffset(0) -{ - auto canvasSize = Application::getInstance()->getCanvasSize(); - resetWidth(canvasSize.x, 0); -} - -void Stats::toggleExpanded() { - _expanded = !_expanded; -} - -// called on mouse click release -// check for clicks over stats in order to expand or contract them -void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) { - auto canvasSize = Application::getInstance()->getCanvasSize(); - - if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) { - // not worried about dragging on stats - return; - } - - int statsHeight = 0, - statsWidth = 0, - statsX = 0, - statsY = 0, - lines = 0; - - statsX = horizontalOffset; - - // top-left stats click - lines = _expanded ? 5 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _generalStatsWidth; - - // ping stats click - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - lines = _expanded ? 4 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _pingStatsWidth; - } - - // geo stats panel click - lines = _expanded ? 4 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } - statsX += _geoStatsWidth; - - // top-right stats click - lines = _expanded ? 11 : 3; - statsHeight = lines * STATS_PELS_PER_LINE + 10; - statsWidth = canvasSize.x - statsX; - if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { - toggleExpanded(); - return; - } -} - -void Stats::resetWidth(int width, int horizontalOffset) { - auto canvasSize = Application::getInstance()->getCanvasSize(); - int extraSpace = canvasSize.x - horizontalOffset - 2 - - STATS_GENERAL_MIN_WIDTH - - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) - - STATS_GEO_MIN_WIDTH - - STATS_OCTREE_MIN_WIDTH; - - int panels = 4; - - _generalStatsWidth = STATS_GENERAL_MIN_WIDTH; - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - _pingStatsWidth = STATS_PING_MIN_WIDTH; - } else { - _pingStatsWidth = 0; - panels = 3; - } - _geoStatsWidth = STATS_GEO_MIN_WIDTH; - _octreeStatsWidth = STATS_OCTREE_MIN_WIDTH; - - if (extraSpace > panels) { - _generalStatsWidth += (int) extraSpace / panels; - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - _pingStatsWidth += (int) extraSpace / panels; - } - _geoStatsWidth += (int) extraSpace / panels; - _octreeStatsWidth += canvasSize.x - - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); - } -} - - -// translucent background box that makes stats more readable -void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) { - glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f, - ((rgba >> 16) & 0xff) / 255.0f, - ((rgba >> 8) & 0xff) / 255.0f, - (rgba & 0xff) / 255.0f); - - // FIX ME: is this correct? It seems to work to fix textures bleeding into us... - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - DependencyManager::get()->renderQuad(x, y, width, height, color); +Stats::Stats(QQuickItem* parent) : QQuickItem(parent) { + INSTANCE = this; + const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + _monospaceFont = font.family(); } bool Stats::includeTimingRecord(const QString& name) { @@ -190,107 +74,240 @@ bool Stats::includeTimingRecord(const QString& name) { return false; } -// display expanded or contracted stats -void Stats::display( - const float* color, - int horizontalOffset, - float fps, - int inPacketsPerSecond, - int outPacketsPerSecond, - int inKbitsPerSecond, - int outKbitsPerSecond, - int voxelPacketsToProcess) -{ - auto canvasSize = Application::getInstance()->getCanvasSize(); - - unsigned int backgroundColor = 0x33333399; - int verticalOffset = 0, lines = 0; - float scale = 0.10f; - float rotation = 0.0f; - int font = 2; - - QLocale locale(QLocale::English); - std::stringstream octreeStats; - - QSharedPointer bandwidthRecorder = DependencyManager::get(); - - if (_lastHorizontalOffset != horizontalOffset) { - resetWidth(canvasSize.x, horizontalOffset); - _lastHorizontalOffset = horizontalOffset; +#define STAT_UPDATE(name, src) \ + { \ + auto val = src; \ + if (_##name != val) { \ + _##name = val; \ + emit name##Changed(); \ + } \ } - glPointSize(1.0f); +#define STAT_UPDATE_FLOAT(name, src, epsilon) \ + { \ + float val = src; \ + if (fabs(_##name - val) >= epsilon) { \ + _##name = val; \ + emit name##Changed(); \ + } \ + } + +void Stats::updateStats() { + if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + if (isVisible()) { + setVisible(false); + } + return; + } else { + if (!isVisible()) { + setVisible(true); + } + } + + bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && + Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded(); + if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { + PerformanceTimer::setActive(shouldDisplayTimingDetail); + } + + + auto nodeList = DependencyManager::get(); + auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves - int totalAvatars = DependencyManager::get()->size() - 1; - int totalServers = DependencyManager::get()->size(); + STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(serverCount, nodeList->size()); + STAT_UPDATE(framerate, (int)qApp->getFps()); - lines = 5; - int columnOneWidth = _generalStatsWidth; + auto bandwidthRecorder = DependencyManager::get(); + STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); + STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); + STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); + + // Second column: ping + if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); + STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); + STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); + + //// Now handle voxel servers, since there could be more than one, we average their ping times + unsigned long totalPingOctree = 0; + int octreeServerCount = 0; + int pingOctreeMax = 0; + int pingVoxel; + nodeList->eachNode([&](const SharedNodePointer& node) { + // TODO: this should also support entities + if (node->getType() == NodeType::EntityServer) { + totalPingOctree += node->getPingMs(); + octreeServerCount++; + if (pingOctreeMax < node->getPingMs()) { + pingOctreeMax = node->getPingMs(); + } + } + }); + + if (octreeServerCount) { + pingVoxel = totalPingOctree / octreeServerCount; + } + + //STAT_UPDATE(entitiesPing, pingVoxel); + //if (_expanded) { + // QString voxelMaxPing; + // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. + // voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); + // } else { + // voxelMaxPing = QString("Voxel max ping: --"); + // } + } else { + // -2 causes the QML to hide the ping column + STAT_UPDATE(audioPing, -2); + } + + + // Third column, avatar stats + MyAvatar* myAvatar = avatarManager->getMyAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); + STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); + STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); + if (_expanded) { + SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); + if (avatarMixer) { + STAT_UPDATE(avatarMixerKbps, roundf( + bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) + + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); + STAT_UPDATE(avatarMixerPps, roundf( + bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) + + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); + } else { + STAT_UPDATE(avatarMixerKbps, -1); + STAT_UPDATE(avatarMixerPps, -1); + } + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + if (audioMixerNode) { + STAT_UPDATE(audioMixerKbps, roundf( + bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerPps, roundf( + bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + } else { + STAT_UPDATE(audioMixerKbps, -1); + STAT_UPDATE(audioMixerPps, -1); + } + + STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size()); + STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); + // TODO fix to match original behavior + //stringstream downloads; + //downloads << "Downloads: "; + //foreach(Resource* resource, ) { + // downloads << (int)(resource->getProgress() * 100.0f) << "% "; + //} + //downloads << "(" << << " pending)"; + } // expanded avatar column + + // Fourth column, octree stats + int serverCount = 0; + int movingServerCount = 0; + unsigned long totalNodes = 0; + unsigned long totalInternal = 0; + unsigned long totalLeaves = 0; + std::stringstream sendingModeStream(""); + sendingModeStream << "["; + NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); + for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + if (_expanded) { + if (serverCount > 1) { + sendingModeStream << ","; + } + if (stats.isMoving()) { + sendingModeStream << "M"; + movingServerCount++; + } else { + sendingModeStream << "S"; + } + } + + // calculate server node totals + totalNodes += stats.getTotalElements(); + if (_expanded) { + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + } + } + if (_expanded) { + if (serverCount == 0) { + sendingModeStream << "---"; + } + sendingModeStream << "] " << serverCount << " servers"; + if (movingServerCount > 0) { + sendingModeStream << " "; + } else { + sendingModeStream << " "; + } + QString sendingModeResult = sendingModeStream.str().c_str(); + STAT_UPDATE(sendingMode, sendingModeResult); + } + + // Incoming packets + QLocale locale(QLocale::English); + auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount(); + if (_expanded) { + std::stringstream octreeStats; + QString packetsString = locale.toString((int)voxelPacketsToProcess); + QString maxString = locale.toString((int)_recentMaxPackets); + octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) + << " [Recent Max: " << qPrintable(maxString) << "]"; + QString str = octreeStats.str().c_str(); + STAT_UPDATE(packetStats, str); + // drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); + } + + if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { + _recentMaxPackets = 0; + _resetRecentMaxPacketsSoon = false; + } + if (voxelPacketsToProcess == 0) { + _resetRecentMaxPacketsSoon = true; + } else if (voxelPacketsToProcess > _recentMaxPackets) { + _recentMaxPackets = voxelPacketsToProcess; + } + + // Server Octree Elements + STAT_UPDATE(serverElements, totalNodes); + STAT_UPDATE(localElements, OctreeElement::getNodeCount()); + + if (_expanded) { + STAT_UPDATE(serverInternal, totalInternal); + STAT_UPDATE(serverLeaves, totalLeaves); + // Local Voxels + STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount()); + STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount()); + // LOD Details + STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get()->getLODFeedbackText()); + } bool performanceTimerIsActive = PerformanceTimer::isActive(); bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); if (displayPerf && performanceTimerIsActive) { + if (!_timingExpanded) { + _timingExpanded = true; + emit timingExpandedChanged(); + } PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up - columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... // we will also include room for 1 line per timing record and a header of 4 lines - lines += 4; - - const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); - QMapIterator i(allRecords); - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); - int statsLines = 0; - while (i.hasNext()) { - i.next(); - if (includeTimingRecord(i.key())) { - lines++; - statsLines++; - if (onlyDisplayTopTen && statsLines == 10) { - break; - } - } - } - } - - drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE); - horizontalOffset += 5; - - int columnOneHorizontalOffset = horizontalOffset; - - QString serverNodes = QString("Servers: %1").arg(totalServers); - QString avatarNodes = QString("Avatars: %1").arg(totalAvatars); - QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0); - - verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line - drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color); - - QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond); - QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2"). - arg((float)inKbitsPerSecond * 1.0f / 1000.0f). - arg((float)outKbitsPerSecond * 1.0f / 1000.0f); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color); - - - // TODO: the display of these timing details should all be moved to JavaScript - if (displayPerf && performanceTimerIsActive) { - bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); // Timing details... - verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, - "-------------------------------------------------------- Function " - "------------------------------------------------------- --msecs- -calls--", color); // First iterate all the records, and for the ones that should be included, insert them into // a new Map sorted by average time... + bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); QMap sortedRecords; const QMap& allRecords = PerformanceTimer::getAllTimerRecords(); QMapIterator i(allRecords); @@ -306,316 +323,54 @@ void Stats::display( int linesDisplayed = 0; QMapIterator j(sortedRecords); j.toBack(); + QString perfLines; while (j.hasPrevious()) { j.previous(); - QChar noBreakingSpace = QChar::Nbsp; - QString functionName = j.value(); + static const QChar noBreakingSpace = QChar::Nbsp; + QString functionName = j.value(); const PerformanceTimerRecord& record = allRecords.value(functionName); - - QString perfLine = QString("%1: %2 [%3]"). - arg(QString(qPrintable(functionName)), 120, noBreakingSpace). + perfLines += QString("%1: %2 [%3]\n"). + arg(QString(qPrintable(functionName)), 90, noBreakingSpace). arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). arg((int)record.getCount(), 6, 10, noBreakingSpace); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color); linesDisplayed++; if (onlyDisplayTopTen && linesDisplayed == 10) { break; } } + _timingStats = perfLines; + emit timingStatsChanged(); + } else if (_timingExpanded) { + _timingExpanded = false; + emit timingExpandedChanged(); } - - - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1; - - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1; - - auto nodeList = DependencyManager::get(); - SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); - SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); - - pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1; - pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; - - // Now handle voxel servers, since there could be more than one, we average their ping times - unsigned long totalPingOctree = 0; - int octreeServerCount = 0; - - nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){ - // TODO: this should also support entities - if (node->getType() == NodeType::EntityServer) { - totalPingOctree += node->getPingMs(); - octreeServerCount++; - if (pingOctreeMax < node->getPingMs()) { - pingOctreeMax = node->getPingMs(); - } - } - }); - - if (octreeServerCount) { - pingVoxel = totalPingOctree / octreeServerCount; - } - - lines = _expanded ? 4 : 3; - - // only draw our background if column one didn't draw a wide background - if (columnOneWidth == _generalStatsWidth) { - drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); - } - horizontalOffset += 5; - - - QString audioPing; - if (pingAudio >= 0) { - audioPing = QString("Audio ping: %1").arg(pingAudio); - } else { - audioPing = QString("Audio ping: --"); - } - - QString avatarPing; - if (pingAvatar >= 0) { - avatarPing = QString("Avatar ping: %1").arg(pingAvatar); - } else { - avatarPing = QString("Avatar ping: --"); - } - - QString voxelAvgPing; - if (pingVoxel >= 0) { - voxelAvgPing = QString("Entities avg ping: %1").arg(pingVoxel); - } else { - voxelAvgPing = QString("Entities avg ping: --"); - } - - drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing.toUtf8().constData(), color); - - if (_expanded) { - QString voxelMaxPing; - if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. - voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); - } else { - voxelMaxPing = QString("Voxel max ping: --"); - } - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color); - } - - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; - } - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 avatarPos = myAvatar->getPosition(); - - lines = _expanded ? 7 : 3; - - if (columnOneWidth == _generalStatsWidth) { - drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); - } - horizontalOffset += 5; - - QString avatarPosition = QString("Position: %1, %2, %3"). - arg(avatarPos.x, -1, 'f', 1). - arg(avatarPos.y, -1, 'f', 1). - arg(avatarPos.z, -1, 'f', 1); - QString avatarVelocity = QString("Velocity: %1").arg(glm::length(myAvatar->getVelocity()), -1, 'f', 1); - QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1); - QString avatarMixerStats; - - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw.toUtf8().constData(), color); - - if (_expanded) { - SharedNodePointer avatarMixer = DependencyManager::get()->soloNodeOfType(NodeType::AvatarMixer); - if (avatarMixer) { - avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps"). - arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))). - arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); - } else { - avatarMixerStats = QString("No Avatar Mixer"); - } - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color); - - stringstream downloads; - downloads << "Downloads: "; - foreach (Resource* resource, ResourceCache::getLoadingRequests()) { - downloads << (int)(resource->getProgress() * 100.0f) << "% "; - } - downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)"; - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); - } - - verticalOffset = STATS_PELS_INITIALOFFSET; - horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; - - lines = _expanded ? 10 : 3; - - drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, - (lines + 1) * STATS_PELS_PER_LINE); - horizontalOffset += 5; - - // Model/Entity render details - octreeStats.str(""); - octreeStats << "Triangles: " << _renderDetails._trianglesRendered - << " / Quads:" << _renderDetails._quadsRendered - << " / Material Switches:" << _renderDetails._materialSwitches; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - if (_expanded) { - octreeStats.str(""); - octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered - << " / Translucent:" << _renderDetails._translucent._rendered; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - octreeStats.str(""); - octreeStats << " Opaque considered: " << _renderDetails._opaque._considered - << " / Out of view:" << _renderDetails._opaque._outOfView - << " / Too small:" << _renderDetails._opaque._tooSmall; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - octreeStats.str(""); - octreeStats << " Translucent considered: " << _renderDetails._translucent._considered - << " / Out of view:" << _renderDetails._translucent._outOfView - << " / Too small:" << _renderDetails._translucent._tooSmall; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - // iterate all the current voxel stats, and list their sending modes, and total voxel counts - std::stringstream sendingMode(""); - sendingMode << "Octree Sending Mode: ["; - int serverCount = 0; - int movingServerCount = 0; - unsigned long totalNodes = 0; - unsigned long totalInternal = 0; - unsigned long totalLeaves = 0; - NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); - for(NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { - //const QUuid& uuid = i->first; - OctreeSceneStats& stats = i->second; - serverCount++; - if (_expanded) { - if (serverCount > 1) { - sendingMode << ","; - } - if (stats.isMoving()) { - sendingMode << "M"; - movingServerCount++; - } else { - sendingMode << "S"; - } - } - - // calculate server node totals - totalNodes += stats.getTotalElements(); - if (_expanded) { - totalInternal += stats.getTotalInternal(); - totalLeaves += stats.getTotalLeaves(); - } - } - if (_expanded) { - if (serverCount == 0) { - sendingMode << "---"; - } - sendingMode << "] " << serverCount << " servers"; - if (movingServerCount > 0) { - sendingMode << " "; - } else { - sendingMode << " "; - } - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color); - } - - // Incoming packets - if (_expanded) { - octreeStats.str(""); - QString packetsString = locale.toString((int)voxelPacketsToProcess); - QString maxString = locale.toString((int)_recentMaxPackets); - octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) - << " [Recent Max: " << qPrintable(maxString) << "]"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { - _recentMaxPackets = 0; - _resetRecentMaxPacketsSoon = false; - } - if (voxelPacketsToProcess == 0) { - _resetRecentMaxPacketsSoon = true; - } else { - if (voxelPacketsToProcess > _recentMaxPackets) { - _recentMaxPackets = voxelPacketsToProcess; - } - } - - QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); - unsigned long localTotal = OctreeElement::getNodeCount(); - QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); - - // Server Octree Elements - if (!_expanded) { - octreeStats.str(""); - octreeStats << "Octree Elements Server: " << qPrintable(serversTotalString) - << " Local:" << qPrintable(localTotalString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - if (_expanded) { - octreeStats.str(""); - octreeStats << "Octree Elements -"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - QString serversInternalString = locale.toString((uint)totalInternal); - QString serversLeavesString = locale.toString((uint)totalLeaves); - - octreeStats.str(""); - octreeStats << " Server: " << qPrintable(serversTotalString) << - " Internal: " << qPrintable(serversInternalString) << - " Leaves: " << qPrintable(serversLeavesString); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - - // Local Voxels - unsigned long localInternal = OctreeElement::getInternalNodeCount(); - unsigned long localLeaves = OctreeElement::getLeafNodeCount(); - QString localInternalString = locale.toString((uint)localInternal); - QString localLeavesString = locale.toString((uint)localLeaves); - - octreeStats.str(""); - octreeStats << " Local: " << qPrintable(serversTotalString) << - " Internal: " << qPrintable(localInternalString) << - " Leaves: " << qPrintable(localLeavesString) << ""; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } - - // LOD Details - octreeStats.str(""); - QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); - octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } +void Stats::setRenderDetails(const RenderDetails& details) { + STAT_UPDATE(triangles, details._trianglesRendered); + STAT_UPDATE(quads, details._quadsRendered); + STAT_UPDATE(materialSwitches, details._materialSwitches); + if (_expanded) { + STAT_UPDATE(meshOpaque, details._opaque._rendered); + STAT_UPDATE(meshTranslucent, details._opaque._rendered); + STAT_UPDATE(opaqueConsidered, details._opaque._considered); + STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView); + STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall); + STAT_UPDATE(translucentConsidered, details._translucent._considered); + STAT_UPDATE(translucentOutOfView, details._translucent._outOfView); + STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall); + } +} + + +/* +// display expanded or contracted stats +void Stats::display( + int voxelPacketsToProcess) +{ + // iterate all the current voxel stats, and list their sending modes, and total voxel counts + +} + + +*/ diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 4c6d6ede4e..a897e0422e 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -1,8 +1,5 @@ // -// Stats.h -// interface/src/ui -// -// Created by Lucas Crisman on 22/03/14. +// Created by Bradley Austin Davis 2015/06/17 // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -13,47 +10,139 @@ #define hifi_Stats_h #include +#include +#include +#include #include -class Stats: public QObject { +#define STATS_PROPERTY(type, name, initialValue) \ + Q_PROPERTY(type name READ name NOTIFY name##Changed) \ +public: \ + type name() { return _##name; }; \ +private: \ + type _##name{ initialValue }; + + +class Stats : public QQuickItem { Q_OBJECT + HIFI_QML_DECL + Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) + Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged) + Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT) + + STATS_PROPERTY(int, serverCount, 0) + STATS_PROPERTY(int, framerate, 0) + STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, packetInCount, 0) + STATS_PROPERTY(int, packetOutCount, 0) + STATS_PROPERTY(float, mbpsIn, 0) + STATS_PROPERTY(float, mbpsOut, 0) + STATS_PROPERTY(int, audioPing, 0) + STATS_PROPERTY(int, avatarPing, 0) + STATS_PROPERTY(int, entitiesPing, 0) + STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) ) + STATS_PROPERTY(float, velocity, 0) + STATS_PROPERTY(float, yaw, 0) + STATS_PROPERTY(int, avatarMixerKbps, 0) + STATS_PROPERTY(int, avatarMixerPps, 0) + STATS_PROPERTY(int, audioMixerKbps, 0) + STATS_PROPERTY(int, audioMixerPps, 0) + STATS_PROPERTY(int, downloads, 0) + STATS_PROPERTY(int, downloadsPending, 0) + STATS_PROPERTY(int, triangles, 0) + STATS_PROPERTY(int, quads, 0) + STATS_PROPERTY(int, materialSwitches, 0) + STATS_PROPERTY(int, meshOpaque, 0) + STATS_PROPERTY(int, meshTranslucent, 0) + STATS_PROPERTY(int, opaqueConsidered, 0) + STATS_PROPERTY(int, opaqueOutOfView, 0) + STATS_PROPERTY(int, opaqueTooSmall, 0) + STATS_PROPERTY(int, translucentConsidered, 0) + STATS_PROPERTY(int, translucentOutOfView, 0) + STATS_PROPERTY(int, translucentTooSmall, 0) + STATS_PROPERTY(QString, sendingMode, QString()) + STATS_PROPERTY(QString, packetStats, QString()) + STATS_PROPERTY(QString, lodStatus, QString()) + STATS_PROPERTY(QString, timingStats, QString()) + STATS_PROPERTY(int, serverElements, 0) + STATS_PROPERTY(int, serverInternal, 0) + STATS_PROPERTY(int, serverLeaves, 0) + STATS_PROPERTY(int, localElements, 0) + STATS_PROPERTY(int, localInternal, 0) + STATS_PROPERTY(int, localLeaves, 0) public: static Stats* getInstance(); - Stats(); - - static void drawBackground(unsigned int rgba, int x, int y, int width, int height); - - void toggleExpanded(); - bool isExpanded() { return _expanded; } - - void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset); - void resetWidth(int width, int horizontalOffset); - void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond, - int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess); + Stats(QQuickItem* parent = nullptr); bool includeTimingRecord(const QString& name); - - void setRenderDetails(const RenderDetails& details) { _renderDetails = details; } - + void setRenderDetails(const RenderDetails& details); + const QString& monospaceFont() { + return _monospaceFont; + } + void updateStats(); + + bool isExpanded() { return _expanded; } + bool isTimingExpanded() { return _timingExpanded; } + + void setExpanded(bool expanded) { + if (_expanded != expanded) { + _expanded = expanded; + emit expandedChanged(); + } + } + +signals: + void expandedChanged(); + void timingExpandedChanged(); + void serverCountChanged(); + void framerateChanged(); + void avatarCountChanged(); + void packetInCountChanged(); + void packetOutCountChanged(); + void mbpsInChanged(); + void mbpsOutChanged(); + void audioPingChanged(); + void avatarPingChanged(); + void entitiesPingChanged(); + void positionChanged(); + void velocityChanged(); + void yawChanged(); + void avatarMixerKbpsChanged(); + void avatarMixerPpsChanged(); + void audioMixerKbpsChanged(); + void audioMixerPpsChanged(); + void downloadsChanged(); + void downloadsPendingChanged(); + void trianglesChanged(); + void quadsChanged(); + void materialSwitchesChanged(); + void meshOpaqueChanged(); + void meshTranslucentChanged(); + void opaqueConsideredChanged(); + void opaqueOutOfViewChanged(); + void opaqueTooSmallChanged(); + void translucentConsideredChanged(); + void translucentOutOfViewChanged(); + void translucentTooSmallChanged(); + void sendingModeChanged(); + void packetStatsChanged(); + void lodStatusChanged(); + void serverElementsChanged(); + void serverInternalChanged(); + void serverLeavesChanged(); + void localElementsChanged(); + void localInternalChanged(); + void localLeavesChanged(); + void timingStatsChanged(); + private: - static Stats* _sharedInstance; - - bool _expanded; - - int _recentMaxPackets; // recent max incoming voxel packets to process - bool _resetRecentMaxPacketsSoon; - - int _generalStatsWidth; - int _bandwidthStatsWidth; - int _pingStatsWidth; - int _geoStatsWidth; - int _octreeStatsWidth; - - int _lastHorizontalOffset; - - RenderDetails _renderDetails; + int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process + bool _resetRecentMaxPacketsSoon{ true }; + bool _expanded{ false }; + bool _timingExpanded{ false }; + QString _monospaceFont; }; #endif // hifi_Stats_h diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index fd2f384fb6..3afd09da65 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -8,9 +8,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "UserInputMapper.h" #include +#include "Application.h" + +#include "UserInputMapper.h" + // UserInputMapper Class @@ -207,6 +210,9 @@ void UserInputMapper::update(float deltaTime) { // Scale all the channel step with the scale for (auto i = 0; i < NUM_ACTIONS; i++) { _actionStates[i] *= _actionScales[i]; + if (_actionStates[i] > 0) { + emit Application::getInstance()->getControllerScriptingInterface()->actionEvent(i, _actionStates[i]); + } } } @@ -241,6 +247,9 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit _actionScales[BOOM_IN] = 1.0f; // 1m per unit _actionScales[BOOM_OUT] = 1.0f; // 1m per unit + _actionStates[SHIFT] = 1.0f; // on + _actionStates[ACTION1] = 1.0f; // default + _actionStates[ACTION2] = 1.0f; // default } // This is only necessary as long as the actions are hardcoded @@ -258,4 +267,7 @@ void UserInputMapper::createActionNames() { _actionNames[PITCH_UP] = "PITCH_UP"; _actionNames[BOOM_IN] = "BOOM_IN"; _actionNames[BOOM_OUT] = "BOOM_OUT"; + _actionNames[SHIFT] = "SHIFT"; + _actionNames[ACTION1] = "ACTION1"; + _actionNames[ACTION2] = "ACTION2"; } \ No newline at end of file diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index 91aa724ea6..800f181dcb 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -139,6 +139,11 @@ public: BOOM_IN, BOOM_OUT, + + SHIFT, + + ACTION1, + ACTION2, NUM_ACTIONS, }; diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index db252d8a04..06e26e0781 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -16,15 +16,12 @@ #include "Application.h" #include "Base3DOverlay.h" -const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f); const float DEFAULT_LINE_WIDTH = 1.0f; const bool DEFAULT_IS_SOLID = false; const bool DEFAULT_IS_DASHED_LINE = false; Base3DOverlay::Base3DOverlay() : - _position(DEFAULT_POSITION), _lineWidth(DEFAULT_LINE_WIDTH), - _rotation(), _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), _ignoreRayIntersection(false), @@ -35,9 +32,8 @@ Base3DOverlay::Base3DOverlay() : Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : Overlay(base3DOverlay), - _position(base3DOverlay->_position), + _transform(base3DOverlay->_transform), _lineWidth(base3DOverlay->_lineWidth), - _rotation(base3DOverlay->_rotation), _isSolid(base3DOverlay->_isSolid), _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), @@ -46,14 +42,6 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : { } -Base3DOverlay::~Base3DOverlay() { -} - -// TODO: Implement accurate getBounds() implementations -AABox Base3DOverlay::getBounds() const { - return AABox(_position, glm::vec3(1.0f)); -} - void Base3DOverlay::setProperties(const QScriptValue& properties) { Overlay::setProperties(properties); @@ -151,13 +139,13 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) { QScriptValue Base3DOverlay::getProperty(const QString& property) { if (property == "position" || property == "start" || property == "p1" || property == "point") { - return vec3toScriptValue(_scriptEngine, _position); + return vec3toScriptValue(_scriptEngine, getPosition()); } if (property == "lineWidth") { return _lineWidth; } if (property == "rotation") { - return quatToScriptValue(_scriptEngine, _rotation); + return quatToScriptValue(_scriptEngine, getRotation()); } if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") { return _isSolid; diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index b24908a0cc..3cb4d27021 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -11,10 +11,7 @@ #ifndef hifi_Base3DOverlay_h #define hifi_Base3DOverlay_h -#include -#include - -#include +#include #include "Overlay.h" @@ -24,32 +21,38 @@ class Base3DOverlay : public Overlay { public: Base3DOverlay(); Base3DOverlay(const Base3DOverlay* base3DOverlay); - ~Base3DOverlay(); // getters virtual bool is3D() const { return true; } - const glm::vec3& getPosition() const { return _position; } - const glm::vec3& getCenter() const { return _position; } // TODO: consider implementing registration points in this class + const glm::vec3& getPosition() const { return _transform.getTranslation(); } + const glm::quat& getRotation() const { return _transform.getRotation(); } + const glm::vec3& getScale() const { return _transform.getScale(); } + + // TODO: consider implementing registration points in this class + const glm::vec3& getCenter() const { return getPosition(); } + float getLineWidth() const { return _lineWidth; } bool getIsSolid() const { return _isSolid; } bool getIsDashedLine() const { return _isDashedLine; } bool getIsSolidLine() const { return !_isDashedLine; } - const glm::quat& getRotation() const { return _rotation; } bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; } bool getDrawInFront() const { return _drawInFront; } bool getDrawOnHUD() const { return _drawOnHUD; } // setters - void setPosition(const glm::vec3& position) { _position = position; } + void setPosition(const glm::vec3& value) { _transform.setTranslation(value); } + void setRotation(const glm::quat& value) { _transform.setRotation(value); } + void setScale(float value) { _transform.setScale(value); } + void setScale(const glm::vec3& value) { _transform.setScale(value); } + void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } - void setRotation(const glm::quat& value) { _rotation = value; } void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; } void setDrawInFront(bool value) { _drawInFront = value; } void setDrawOnHUD(bool value) { _drawOnHUD = value; } - virtual AABox getBounds() const; + virtual AABox getBounds() const = 0; virtual void setProperties(const QScriptValue& properties); virtual QScriptValue getProperty(const QString& property); @@ -62,9 +65,9 @@ public: } protected: - glm::vec3 _position; + Transform _transform; + float _lineWidth; - glm::quat _rotation; bool _isSolid; bool _isDashedLine; bool _ignoreRayIntersection; diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index e7b043f44f..288a950bbb 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -9,26 +9,20 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Application.h" -#include "GeometryUtil.h" -#include "PlaneShape.h" - #include "BillboardOverlay.h" -BillboardOverlay::BillboardOverlay() : - _fromImage(), - _scale(1.0f), - _isFacingAvatar(true) -{ +#include "Application.h" +#include "GeometryUtil.h" + +BillboardOverlay::BillboardOverlay() { _isLoaded = false; } BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : - Base3DOverlay(billboardOverlay), + Planar3DOverlay(billboardOverlay), _url(billboardOverlay->_url), _texture(billboardOverlay->_texture), _fromImage(billboardOverlay->_fromImage), - _scale(billboardOverlay->_scale), _isFacingAvatar(billboardOverlay->_isFacingAvatar) { } @@ -46,8 +40,8 @@ void BillboardOverlay::render(RenderArgs* args) { 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 = args->_viewFrustum->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); rotation *= getRotation(); } else { rotation = getRotation(); @@ -89,11 +83,9 @@ void BillboardOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - Transform transform; - transform.setTranslation(_position); - transform.setRotation(rotation); - transform.setScale(_scale); - + Transform transform = _transform; + transform.postScale(glm::vec3(getDimensions(), 1.0f)); + batch->setModelTransform(transform); batch->setUniformTexture(0, _texture->getGPUTexture()); @@ -111,10 +103,10 @@ void BillboardOverlay::render(RenderArgs* args) { glBindTexture(GL_TEXTURE_2D, _texture->getID()); glPushMatrix(); { - glTranslatef(_position.x, _position.y, _position.z); + glTranslatef(getPosition().x, getPosition().y, getPosition().z); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(_scale, _scale, _scale); + glScalef(_dimensions.x, _dimensions.y, 1.0f); DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); @@ -130,7 +122,7 @@ void BillboardOverlay::render(RenderArgs* args) { } void BillboardOverlay::setProperties(const QScriptValue &properties) { - Base3DOverlay::setProperties(properties); + Planar3DOverlay::setProperties(properties); QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { @@ -171,11 +163,6 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } - QScriptValue scaleValue = properties.property("scale"); - if (scaleValue.isValid()) { - _scale = scaleValue.toVariant().toFloat(); - } - QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); @@ -189,14 +176,11 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { if (property == "subImage") { return qRectToScriptValue(_scriptEngine, _fromImage); } - if (property == "scale") { - return _scale; - } if (property == "isFacingAvatar") { return _isFacingAvatar; } - return Base3DOverlay::getProperty(property); + return Planar3DOverlay::getProperty(property); } void BillboardOverlay::setURL(const QString& url) { @@ -212,13 +196,11 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float& distance, BoxFace& face) { if (_texture) { - glm::quat rotation; + glm::quat rotation = getRotation(); 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)); - } else { - rotation = _rotation; } // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. @@ -226,9 +208,9 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float width = isNull ? _texture->getWidth() : _fromImage.width(); float height = isNull ? _texture->getHeight() : _fromImage.height(); float maxSize = glm::max(width, height); - glm::vec2 dimensions = _scale * glm::vec2(width / maxSize, height / maxSize); + glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - return findRayRectangleIntersection(origin, direction, rotation, _position, dimensions, distance); + return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance); } return false; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index a09c0c7528..15be0419a9 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -12,14 +12,11 @@ #ifndef hifi_BillboardOverlay_h #define hifi_BillboardOverlay_h -#include -#include - #include -#include "Base3DOverlay.h" +#include "Planar3DOverlay.h" -class BillboardOverlay : public Base3DOverlay { +class BillboardOverlay : public Planar3DOverlay { Q_OBJECT public: BillboardOverlay(); @@ -29,7 +26,6 @@ public: // setters void setURL(const QString& url); - void setScale(float scale) { _scale = scale; } void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } virtual void setProperties(const QScriptValue& properties); @@ -48,8 +44,7 @@ private: QRect _fromImage; // where from in the image to sample - float _scale; - bool _isFacingAvatar; + bool _isFacingAvatar = true; }; #endif // hifi_BillboardOverlay_h diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 034095291f..53f1b4ce21 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -8,16 +8,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" +#include "Circle3DOverlay.h" #include #include -#include -#include -#include +#include -#include "Circle3DOverlay.h" Circle3DOverlay::Circle3DOverlay() : _startAt(0.0f), @@ -66,9 +62,6 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : { } -Circle3DOverlay::~Circle3DOverlay() { -} - void Circle3DOverlay::render(RenderArgs* args) { if (!_visible) { return; // do nothing if we're not visible @@ -103,15 +96,13 @@ void Circle3DOverlay::render(RenderArgs* args) { _lastColor = colorX; auto geometryCache = DependencyManager::get(); - - Transform transform; - transform.setTranslation(getCenter()); - transform.setRotation(getRotation()); - transform.setScale(glm::vec3(getDimensions(), 0.01f)); - + Q_ASSERT(args->_batch); auto& batch = *args->_batch; batch._glLineWidth(_lineWidth); + + auto transform = _transform; + transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch.setModelTransform(transform); DependencyManager::get()->bindSimpleProgram(batch, false, false); @@ -402,12 +393,12 @@ bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, if (intersects) { glm::vec3 hitPosition = origin + (distance * direction); - glm::vec3 localHitPosition = glm::inverse(_rotation) * (hitPosition - _position); - localHitPosition.y = localHitPosition.y * _dimensions.x / _dimensions.y; // Scale to make circular + glm::vec3 localHitPosition = glm::inverse(getRotation()) * (hitPosition - getPosition()); + localHitPosition.y = localHitPosition.y * getDimensions().x / getDimensions().y; // Scale to make circular float distanceToHit = glm::length(localHitPosition); - float innerRadius = _dimensions.x / 2.0f * _innerRadius; - float outerRadius = _dimensions.x / 2.0f * _outerRadius; + float innerRadius = getDimensions().x / 2.0f * _innerRadius; + float outerRadius = getDimensions().x / 2.0f * _outerRadius; intersects = innerRadius <= distanceToHit && distanceToHit <= outerRadius; } diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index fa9ecd0f25..0a2352148a 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -11,6 +11,9 @@ #ifndef hifi_Circle3DOverlay_h #define hifi_Circle3DOverlay_h +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include "Planar3DOverlay.h" class Circle3DOverlay : public Planar3DOverlay { @@ -19,7 +22,7 @@ class Circle3DOverlay : public Planar3DOverlay { public: Circle3DOverlay(); Circle3DOverlay(const Circle3DOverlay* circle3DOverlay); - ~Circle3DOverlay(); + virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); virtual QScriptValue getProperty(const QString& property); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 37b30a5bcb..7eb9a5b414 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -19,17 +19,11 @@ #include "Application.h" #include "Cube3DOverlay.h" -Cube3DOverlay::Cube3DOverlay() : _borderSize(0) { -} - Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) : Volume3DOverlay(cube3DOverlay) { } -Cube3DOverlay::~Cube3DOverlay() { -} - void Cube3DOverlay::render(RenderArgs* args) { if (!_visible) { return; // do nothing if we're not visible diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index 16705a9c71..397ad77a9e 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -17,9 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay { Q_OBJECT public: - Cube3DOverlay(); + Cube3DOverlay() {} Cube3DOverlay(const Cube3DOverlay* cube3DOverlay); - ~Cube3DOverlay(); + virtual void render(RenderArgs* args); virtual Cube3DOverlay* createClone() const; diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index e68e5b47f2..6316c8cd77 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -9,28 +9,29 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include "Grid3DOverlay.h" + #include #include "Application.h" -#include "Grid3DOverlay.h" ProgramObject Grid3DOverlay::_gridProgram; -Grid3DOverlay::Grid3DOverlay() : Base3DOverlay(), +Grid3DOverlay::Grid3DOverlay() : _minorGridWidth(1.0), _majorGridEvery(5) { } Grid3DOverlay::Grid3DOverlay(const Grid3DOverlay* grid3DOverlay) : - Base3DOverlay(grid3DOverlay), + Planar3DOverlay(grid3DOverlay), _minorGridWidth(grid3DOverlay->_minorGridWidth), _majorGridEvery(grid3DOverlay->_majorGridEvery) { } -Grid3DOverlay::~Grid3DOverlay() { -} - void Grid3DOverlay::render(RenderArgs* args) { if (!_visible) { return; // do nothing if we're not visible @@ -41,7 +42,7 @@ void Grid3DOverlay::render(RenderArgs* args) { 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(); + glm::vec3 rotated = glm::inverse(getRotation()) * Application::getInstance()->getCamera()->getPosition(); float spacing = _minorGridWidth; @@ -53,7 +54,7 @@ void Grid3DOverlay::render(RenderArgs* args) { if (batch) { Transform transform; - transform.setRotation(_rotation); + transform.setRotation(getRotation()); // Minor grid @@ -61,7 +62,7 @@ void Grid3DOverlay::render(RenderArgs* args) { 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); + getPosition().z); float scale = MINOR_GRID_DIVISIONS * spacing; transform.setTranslation(position); @@ -78,7 +79,7 @@ void Grid3DOverlay::render(RenderArgs* args) { 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); + getPosition().z); float scale = MAJOR_GRID_DIVISIONS * spacing; transform.setTranslation(position); @@ -161,7 +162,7 @@ void Grid3DOverlay::render(RenderArgs* args) { } void Grid3DOverlay::setProperties(const QScriptValue& properties) { - Base3DOverlay::setProperties(properties); + Planar3DOverlay::setProperties(properties); if (properties.property("minorGridWidth").isValid()) { _minorGridWidth = properties.property("minorGridWidth").toVariant().toFloat(); @@ -180,7 +181,7 @@ QScriptValue Grid3DOverlay::getProperty(const QString& property) { return _majorGridEvery; } - return Base3DOverlay::getProperty(property); + return Planar3DOverlay::getProperty(property); } Grid3DOverlay* Grid3DOverlay::createClone() const { diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 451ce0a498..ac0f2834fe 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -15,20 +15,16 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include - #include -#include -#include "Base3DOverlay.h" +#include "Planar3DOverlay.h" -class Grid3DOverlay : public Base3DOverlay { +class Grid3DOverlay : public Planar3DOverlay { Q_OBJECT public: Grid3DOverlay(); Grid3DOverlay(const Grid3DOverlay* grid3DOverlay); - ~Grid3DOverlay(); virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 72e47b5e3d..399e8a459a 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -8,17 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include +#include "ImageOverlay.h" #include #include -#include - -#include "ImageOverlay.h" +#include ImageOverlay::ImageOverlay() : _imageURL(), @@ -38,9 +32,6 @@ ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : { } -ImageOverlay::~ImageOverlay() { -} - // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 31c6031102..b7924bdb71 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -15,17 +15,11 @@ #include "InterfaceConfig.h" #include -#include #include -#include -#include #include -#include -#include #include -#include "Overlay.h" #include "Overlay2D.h" class ImageOverlay : public Overlay2D { @@ -34,7 +28,7 @@ class ImageOverlay : public Overlay2D { public: ImageOverlay(); ImageOverlay(const ImageOverlay* imageOverlay); - ~ImageOverlay(); + virtual void render(RenderArgs* args); // getters diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 44c6ec7ef6..0bb32b9d6e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Line3DOverlay.h" @@ -33,13 +34,12 @@ Line3DOverlay::~Line3DOverlay() { } AABox Line3DOverlay::getBounds() const { - auto start = _position + _start; - auto end = _position + _end; - - auto min = glm::min(start, end); - auto max = glm::max(start, end); - - return AABox(min, max - min); + auto extents = Extents{}; + extents.addPoint(_start); + extents.addPoint(_end); + extents.transform(_transform); + + return AABox(extents); } void Line3DOverlay::render(RenderArgs* args) { @@ -55,14 +55,11 @@ void Line3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - Transform transform; - transform.setTranslation(_position); - transform.setRotation(_rotation); - batch->setModelTransform(transform); + batch->setModelTransform(_transform); if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() - DependencyManager::get()->renderDashedLine(*batch, _position, _end, colorv4, _geometryCacheID); + DependencyManager::get()->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID); } else { DependencyManager::get()->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); } @@ -87,7 +84,7 @@ void Line3DOverlay::render(RenderArgs* args) { if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() - DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + DependencyManager::get()->renderDashedLine(_start, _end, colorv4, _geometryCacheID); } else { DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); } diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index ac5f18d8ed..4a4d8f4d90 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -21,7 +21,7 @@ public: Line3DOverlay(const Line3DOverlay* line3DOverlay); ~Line3DOverlay(); virtual void render(RenderArgs* args); - virtual AABox getBounds() const override; + virtual AABox getBounds() const; // getters const glm::vec3& getStart() const { return _start; } diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index b9ce245128..912196041f 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -24,10 +24,6 @@ LocalModelsOverlay::LocalModelsOverlay(const LocalModelsOverlay* localModelsOver Volume3DOverlay(localModelsOverlay), _entityTreeRenderer(localModelsOverlay->_entityTreeRenderer) { - -} - -LocalModelsOverlay::~LocalModelsOverlay() { } void LocalModelsOverlay::update(float deltatime) { @@ -46,7 +42,7 @@ void LocalModelsOverlay::render(RenderArgs* args) { glPushMatrix(); { Application* app = Application::getInstance(); glm::vec3 oldTranslation = app->getViewMatrixTranslation(); - app->setViewMatrixTranslation(oldTranslation + _position); + app->setViewMatrixTranslation(oldTranslation + getPosition()); _entityTreeRenderer->render(args); Application::getInstance()->setViewMatrixTranslation(oldTranslation); } glPopMatrix(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index a82943a4a0..c311b2bc1b 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -21,7 +21,6 @@ class LocalModelsOverlay : public Volume3DOverlay { public: LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); - ~LocalModelsOverlay(); virtual void update(float deltatime); virtual void render(RenderArgs* args); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 822fff9808..4476518efe 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -9,15 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ModelOverlay.h" + #include #include -#include "ModelOverlay.h" ModelOverlay::ModelOverlay() : _model(), _modelTextures(QVariantMap()), - _scale(1.0f), _updateModel(false) { _model.init(); @@ -25,12 +25,10 @@ ModelOverlay::ModelOverlay() } ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : - Base3DOverlay(modelOverlay), + Volume3DOverlay(modelOverlay), _model(), _modelTextures(QVariantMap()), _url(modelOverlay->_url), - _rotation(modelOverlay->_rotation), - _scale(modelOverlay->_scale), _updateModel(false) { _model.init(); @@ -45,8 +43,9 @@ void ModelOverlay::update(float deltatime) { _updateModel = false; _model.setSnapModelToCenter(true); - _model.setRotation(_rotation); - _model.setTranslation(_position); + _model.setScale(getScale()); + _model.setRotation(getRotation()); + _model.setTranslation(getPosition()); _model.setURL(_url); _model.simulate(deltatime, true); } else { @@ -56,13 +55,13 @@ void ModelOverlay::update(float deltatime) { } bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { - Base3DOverlay::addToScene(overlay, scene, pendingChanges); + Volume3DOverlay::addToScene(overlay, scene, pendingChanges); _model.addToScene(scene, pendingChanges); return true; } void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges) { - Base3DOverlay::removeFromScene(overlay, scene, pendingChanges); + Volume3DOverlay::removeFromScene(overlay, scene, pendingChanges); _model.removeFromScene(scene, pendingChanges); } @@ -100,54 +99,26 @@ void ModelOverlay::render(RenderArgs* args) { } void ModelOverlay::setProperties(const QScriptValue &properties) { - Base3DOverlay::setProperties(properties); + auto position = getPosition(); + auto rotation = getRotation(); + auto scale = getDimensions(); + + Volume3DOverlay::setProperties(properties); + + if (position != getPosition() || rotation != getRotation() || scale != getDimensions()) { + _model.setScaleToFit(true, getScale()); + _updateModel = true; + } QScriptValue urlValue = properties.property("url"); - if (urlValue.isValid()) { - _url = urlValue.toVariant().toString(); + if (urlValue.isValid() && urlValue.isString()) { + _url = urlValue.toString(); _updateModel = true; _isLoaded = false; } - QScriptValue scaleValue = properties.property("scale"); - if (scaleValue.isValid()) { - _scale = scaleValue.toVariant().toFloat(); - _model.setScaleToFit(true, _scale); - _updateModel = true; - } - - QScriptValue rotationValue = properties.property("rotation"); - if (rotationValue.isValid()) { - QScriptValue x = rotationValue.property("x"); - QScriptValue y = rotationValue.property("y"); - QScriptValue z = rotationValue.property("z"); - QScriptValue w = rotationValue.property("w"); - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - _rotation.x = x.toVariant().toFloat(); - _rotation.y = y.toVariant().toFloat(); - _rotation.z = z.toVariant().toFloat(); - _rotation.w = w.toVariant().toFloat(); - } - _updateModel = true; - } - - QScriptValue dimensionsValue = properties.property("dimensions"); - if (dimensionsValue.isValid()) { - QScriptValue x = dimensionsValue.property("x"); - QScriptValue y = dimensionsValue.property("y"); - QScriptValue z = dimensionsValue.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 dimensions; - dimensions.x = x.toVariant().toFloat(); - dimensions.y = y.toVariant().toFloat(); - dimensions.z = z.toVariant().toFloat(); - _model.setScaleToFit(true, dimensions); - } - _updateModel = true; - } - QScriptValue texturesValue = properties.property("textures"); - if (texturesValue.isValid()) { + if (texturesValue.isValid() && texturesValue.toVariant().canConvert(QVariant::Map)) { QVariantMap textureMap = texturesValue.toVariant().toMap(); foreach(const QString& key, textureMap.keys()) { @@ -161,22 +132,12 @@ void ModelOverlay::setProperties(const QScriptValue &properties) { _modelTextures[key] = newTextureURL; // Keep local track of textures for getProperty() } } - - if (properties.property("position").isValid()) { - _updateModel = true; - } } QScriptValue ModelOverlay::getProperty(const QString& property) { if (property == "url") { return _url.toString(); } - if (property == "scale") { - return _scale; - } - if (property == "rotation") { - return quatToScriptValue(_scriptEngine, _rotation); - } if (property == "dimensions") { return vec3toScriptValue(_scriptEngine, _model.getScaleToFitDimensions()); } @@ -192,7 +153,7 @@ QScriptValue ModelOverlay::getProperty(const QString& property) { } } - return Base3DOverlay::getProperty(property); + return Volume3DOverlay::getProperty(property); } bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index a81cae530a..1c43f42909 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -14,9 +14,9 @@ #include -#include "Base3DOverlay.h" +#include "Volume3DOverlay.h" -class ModelOverlay : public Base3DOverlay { +class ModelOverlay : public Volume3DOverlay { Q_OBJECT public: ModelOverlay(); @@ -41,9 +41,6 @@ private: QVariantMap _modelTextures; QUrl _url; - glm::quat _rotation; - float _scale; - bool _updateModel; }; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 745c2b4a10..17e4ae74e1 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -8,12 +8,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - #include "Overlay.h" #include +#include + +static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float DEFAULT_ALPHA = 0.7f; Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 0264c6e3c0..375a1c3bb9 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -14,18 +14,11 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include -#include -#include - -#include #include // for xColor -#include -#include #include -const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; -const float DEFAULT_ALPHA = 0.7f; +class QScriptEngine; +class QScriptValue; class Overlay : public QObject { Q_OBJECT @@ -37,7 +30,6 @@ public: }; typedef std::shared_ptr Pointer; - typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; @@ -47,6 +39,8 @@ public: void init(QScriptEngine* scriptEngine); virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; + + virtual AABox getBounds() const = 0; virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); diff --git a/interface/src/ui/overlays/Overlay2D.cpp b/interface/src/ui/overlays/Overlay2D.cpp index f60d44a472..e37e70700c 100644 --- a/interface/src/ui/overlays/Overlay2D.cpp +++ b/interface/src/ui/overlays/Overlay2D.cpp @@ -8,16 +8,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include - #include "Overlay2D.h" - -Overlay2D::Overlay2D() { -} +#include Overlay2D::Overlay2D(const Overlay2D* overlay2D) : Overlay(overlay2D), @@ -25,7 +18,9 @@ Overlay2D::Overlay2D(const Overlay2D* overlay2D) : { } -Overlay2D::~Overlay2D() { +AABox Overlay2D::getBounds() const { + return AABox(glm::vec3(_bounds.x(), _bounds.y(), 0.0f), + glm::vec3(_bounds.width(), _bounds.height(), 0.01f)); } void Overlay2D::setProperties(const QScriptValue& properties) { @@ -40,7 +35,7 @@ void Overlay2D::setProperties(const QScriptValue& properties) { boundsRect.setHeight(bounds.property("height").toVariant().toInt()); setBounds(boundsRect); } else { - QRect oldBounds = getBounds(); + QRect oldBounds = _bounds; QRect newBounds = oldBounds; if (properties.property("x").isValid()) { diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h index 20641206c2..d91a20b070 100644 --- a/interface/src/ui/overlays/Overlay2D.h +++ b/interface/src/ui/overlays/Overlay2D.h @@ -15,10 +15,6 @@ #include "InterfaceConfig.h" #include -#include -#include - -#include // for xColor #include "Overlay.h" @@ -26,9 +22,10 @@ class Overlay2D : public Overlay { Q_OBJECT public: - Overlay2D(); + Overlay2D() {} Overlay2D(const Overlay2D* overlay2D); - ~Overlay2D(); + + virtual AABox getBounds() const; virtual bool is3D() const { return false; } @@ -37,8 +34,8 @@ public: int getY() const { return _bounds.y(); } int getWidth() const { return _bounds.width(); } int getHeight() const { return _bounds.height(); } - const QRect& getBounds() const { return _bounds; } - + const QRect& getBoundingRect() const { return _bounds; } + // setters void setX(int x) { _bounds.setX(x); } void setY(int y) { _bounds.setY(y); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 6e579ed4c4..dc49478f38 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -8,14 +8,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Overlays.h" + #include #include -#include #include -#include -#include #include #include "BillboardOverlay.h" @@ -25,7 +24,6 @@ #include "Line3DOverlay.h" #include "LocalModelsOverlay.h" #include "ModelOverlay.h" -#include "Overlays.h" #include "Rectangle3DOverlay.h" #include "Sphere3DOverlay.h" #include "Grid3DOverlay.h" @@ -37,7 +35,6 @@ Overlays::Overlays() : _nextOverlayID(1) { } Overlays::~Overlays() { - { QWriteLocker lock(&_lock); QWriteLocker deleteLock(&_deleteLock); @@ -99,9 +96,6 @@ void Overlays::cleanupOverlaysToDelete() { void Overlays::renderHUD(RenderArgs* renderArgs) { QReadLocker lock(&_lock); - - auto lodManager = DependencyManager::get(); - foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { if (thisOverlay->is3D()) { glEnable(GL_DEPTH_TEST); @@ -258,7 +252,7 @@ void Overlays::deleteOverlay(unsigned int id) { unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { - pointCopy = qApp->getApplicationOverlay().screenToOverlay(point); + pointCopy = qApp->getApplicationCompositor().screenToOverlay(point); } QReadLocker lock(&_lock); @@ -284,7 +278,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { } else { Overlay2D* thisOverlay = static_cast(i.value().get()); if (thisOverlay->getVisible() && thisOverlay->isLoaded() && - thisOverlay->getBounds().contains(pointCopy.x, pointCopy.y, false)) { + thisOverlay->getBoundingRect().contains(pointCopy.x, pointCopy.y, false)) { return thisID; } } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 57d8ccd509..cd5b0f1d10 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -11,13 +11,13 @@ #ifndef hifi_Overlays_h #define hifi_Overlays_h -#include +#include #include -#include -#include "Base3DOverlay.h" #include "Overlay.h" +class PickRay; + class OverlayPropertyResult { public: OverlayPropertyResult(); @@ -48,9 +48,11 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R class Overlays : public QObject { Q_OBJECT + public: Overlays(); ~Overlays(); + void init(); void update(float deltatime); void renderHUD(RenderArgs* renderArgs); diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index bcfba67313..b7cacef80c 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -35,8 +35,8 @@ namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { - if (overlay->is3D() && !static_cast(overlay.get())->getDrawOnHUD()) { - if (static_cast(overlay.get())->getDrawInFront()) { + if (overlay->is3D() && !std::dynamic_pointer_cast(overlay)->getDrawOnHUD()) { + if (std::dynamic_pointer_cast(overlay)->getDrawInFront()) { return ItemKey::Builder().withTypeShape().withLayered().build(); } else { return ItemKey::Builder::opaqueShape(); @@ -46,12 +46,7 @@ namespace render { } } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { - if (overlay->is3D()) { - return static_cast(overlay.get())->getBounds(); - } else { - QRect bounds = static_cast(overlay.get())->getBounds(); - return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f)); - } + return overlay->getBounds(); } template <> int payloadGetLayer(const Overlay::Pointer& overlay) { // MAgic number while we are defining the layering mechanism: @@ -59,7 +54,7 @@ namespace render { const int LAYER_3D_FRONT = 1; const int LAYER_3D = 0; if (overlay->is3D()) { - return (static_cast(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D); + return (std::dynamic_pointer_cast(overlay)->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D); } else { return LAYER_2D; } diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 7ed7332f19..0ca092ba23 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -8,32 +8,24 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include -#include -#include - -#include "GeometryUtil.h" - #include "Planar3DOverlay.h" -const float DEFAULT_SIZE = 1.0f; - -Planar3DOverlay::Planar3DOverlay() : - _dimensions(glm::vec2(DEFAULT_SIZE, DEFAULT_SIZE)) -{ -} +#include +#include +#include Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : - Base3DOverlay(planar3DOverlay), - _dimensions(planar3DOverlay->_dimensions) + Base3DOverlay(planar3DOverlay) { } -Planar3DOverlay::~Planar3DOverlay() { +AABox Planar3DOverlay::getBounds() const { + auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f}; + + auto extents = Extents{-halfDimensions, halfDimensions}; + extents.transform(_transform); + + return AABox(extents); } void Planar3DOverlay::setProperties(const QScriptValue& properties) { @@ -86,7 +78,7 @@ void Planar3DOverlay::setProperties(const QScriptValue& properties) { QScriptValue Planar3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { - return vec2toScriptValue(_scriptEngine, _dimensions); + return vec2toScriptValue(_scriptEngine, getDimensions()); } return Base3DOverlay::getProperty(property); @@ -94,5 +86,5 @@ QScriptValue Planar3DOverlay::getProperty(const QString& property) { bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { - return findRayRectangleIntersection(origin, direction, _rotation, _position, _dimensions, distance); + return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getDimensions(), distance); } diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 8a02b35bd2..9683cb3357 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -14,34 +14,28 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include - -#include - #include "Base3DOverlay.h" class Planar3DOverlay : public Base3DOverlay { Q_OBJECT public: - Planar3DOverlay(); + Planar3DOverlay() {} Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); - ~Planar3DOverlay(); - - // getters - const glm::vec2& getDimensions() const { return _dimensions; } - - // setters - void setSize(float size) { _dimensions = glm::vec2(size, size); } + + AABox getBounds() const; + + glm::vec2 getDimensions() const { return _dimensions; } + void setDimensions(float value) { _dimensions = glm::vec2(value); } void setDimensions(const glm::vec2& value) { _dimensions = value; } - + virtual void setProperties(const QScriptValue& properties); virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); - + protected: - glm::vec2 _dimensions; + glm::vec2 _dimensions{1.0f, 1.0f}; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index dc5fdeabb2..74bbd1bca8 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -11,12 +11,12 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" +#include "Rectangle3DOverlay.h" + #include #include #include -#include "Rectangle3DOverlay.h" - Rectangle3DOverlay::Rectangle3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index f5fba0ed05..e129954db9 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -17,17 +17,12 @@ #include "Sphere3DOverlay.h" #include "Application.h" -Sphere3DOverlay::Sphere3DOverlay() { -} Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : Volume3DOverlay(Sphere3DOverlay) { } -Sphere3DOverlay::~Sphere3DOverlay() { -} - void Sphere3DOverlay::render(RenderArgs* args) { if (!_visible) { return; // do nothing if we're not visible @@ -42,11 +37,8 @@ void Sphere3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - Transform transform; - transform.setTranslation(_position); - transform.setRotation(_rotation); - transform.setScale(_dimensions); - + Transform transform = _transform; + transform.postScale(getDimensions()); batch->setModelTransform(transform); DependencyManager::get()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid); } else { diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index 3b881e5ba6..b82dc548f1 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -17,9 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay { Q_OBJECT public: - Sphere3DOverlay(); + Sphere3DOverlay() {} Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay); - ~Sphere3DOverlay(); + virtual void render(RenderArgs* args); virtual Sphere3DOverlay* createClone() const; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index fd8410b7e6..37774094de 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -11,13 +11,18 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include "Application.h" #include "Text3DOverlay.h" +#include +#include + +#include "Application.h" + const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const float DEFAULT_BACKGROUND_ALPHA = 0.7f; const float DEFAULT_MARGIN = 0.1f; -const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation +const int FIXED_FONT_POINT_SIZE = 40; +const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation const float LINE_SCALE_RATIO = 1.2f; Text3DOverlay::Text3DOverlay() : @@ -30,6 +35,7 @@ Text3DOverlay::Text3DOverlay() : _bottomMargin(DEFAULT_MARGIN), _isFacingAvatar(false) { + _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); } Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : @@ -44,6 +50,7 @@ Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : _bottomMargin(text3DOverlay->_bottomMargin), _isFacingAvatar(text3DOverlay->_isFacingAvatar) { + _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); } Text3DOverlay::~Text3DOverlay() { @@ -52,7 +59,7 @@ Text3DOverlay::~Text3DOverlay() { xColor Text3DOverlay::getBackgroundColor() { if (_colorPulse == 0.0f) { - return _backgroundColor; + return _backgroundColor; } float pulseLevel = updatePulse(); @@ -75,72 +82,63 @@ void Text3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - 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(); - } else { - rotation = getRotation(); - } - - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - const float MAX_COLOR = 255.0f; - xColor backgroundColor = getBackgroundColor(); - glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, - getBackgroundAlpha()); - - glm::vec2 dimensions = getDimensions(); - glm::vec2 halfDimensions = dimensions * 0.5f; - - const float SLIGHTLY_BEHIND = -0.005f; - - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); - DependencyManager::get()->renderQuad(topLeft, bottomRight, quadColor); - - // Same font properties as textSize() - float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - - float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - - glTranslatef(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.0f); - - glm::vec2 clipMinimum(0.0f, 0.0f); - glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, - (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); - - glScalef(scaleFactor, -scaleFactor, scaleFactor); - enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipDimensions.x); - enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); - enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipDimensions.y); - enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); - glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, getAlpha() }; - _textRenderer->draw(0, 0, _text, textColor); - - glDisable(GL_CLIP_PLANE0); - glDisable(GL_CLIP_PLANE1); - glDisable(GL_CLIP_PLANE2); - glDisable(GL_CLIP_PLANE3); - - } glPopMatrix(); + Q_ASSERT(args->_batch); + auto& batch = *args->_batch; -} - -void Text3DOverlay::enableClipPlane(GLenum plane, float x, float y, float z, float w) { - GLdouble coefficients[] = { x, y, z, w }; - glClipPlane(plane, coefficients); - glEnable(plane); + glm::quat rotation; + + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = args->_viewFrustum->getOrientation(); + } else { + rotation = getRotation(); + } + + Transform transform; + transform.setTranslation(getPosition()); + transform.setRotation(rotation); + transform.setScale(getScale()); + + batch.setModelTransform(transform); + + const float MAX_COLOR = 255.0f; + xColor backgroundColor = getBackgroundColor(); + glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, + getBackgroundAlpha()); + + glm::vec2 dimensions = getDimensions(); + glm::vec2 halfDimensions = dimensions * 0.5f; + + const float SLIGHTLY_BEHIND = -0.005f; + + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor); + + // Same font properties as textSize() + float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; + + float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; + + glm::vec2 clipMinimum(0.0f, 0.0f); + glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, + (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); + + transform.setTranslation(getPosition()); + transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin) , halfDimensions.y - _topMargin, 0.01f)); + transform.setScale(scaleFactor); + batch.setModelTransform(transform); + + glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, getAlpha() }; + _textRenderer->draw(batch, 0, 0, _text, textColor); + + batch.setPipeline(DrawOverlay3D::getOpaquePipeline()); } void Text3DOverlay::setProperties(const QScriptValue& properties) { Planar3DOverlay::setProperties(properties); - + QScriptValue text = properties.property("text"); if (text.isValid()) { setText(text.toVariant().toString()); diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 77a5c23cb9..93d4106708 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -16,12 +16,9 @@ #include -#include -#include - #include "Planar3DOverlay.h" -const int FIXED_FONT_POINT_SIZE = 40; +class TextRenderer3D; class Text3DOverlay : public Planar3DOverlay { Q_OBJECT @@ -60,9 +57,7 @@ public: virtual Text3DOverlay* createClone() const; private: - void enableClipPlane(GLenum plane, float x, float y, float z, float w); - - TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); + TextRenderer3D* _textRenderer = nullptr; QString _text; xColor _backgroundColor; diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index e709bbd9fc..ccad3bd295 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -11,11 +11,14 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" +#include "TextOverlay.h" + #include #include +#include #include +#include -#include "TextOverlay.h" TextOverlay::TextOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), @@ -80,6 +83,7 @@ void TextOverlay::render(RenderArgs* args) { glm::vec2 topLeft(left, top); glm::vec2 bottomRight(right, bottom); + glBindTexture(GL_TEXTURE_2D, 0); DependencyManager::get()->renderQuad(topLeft, bottomRight, quadColor); const int leftAdjust = -1; // required to make text render relative to left edge of bounds diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 5a715ebfdf..db89f0b73d 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -14,14 +14,10 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include -#include #include #include -#include -#include "Overlay.h" #include "Overlay2D.h" const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; @@ -30,6 +26,8 @@ const int DEFAULT_MARGIN = 10; const int DEFAULT_FONTSIZE = 11; const int DEFAULT_FONT_WEIGHT = 50; +class TextRenderer; + class TextOverlay : public Overlay2D { Q_OBJECT diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index b0310f8155..a7c9835f5d 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -8,32 +8,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include - -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include -#include - #include "Volume3DOverlay.h" -const float DEFAULT_SIZE = 1.0f; - -Volume3DOverlay::Volume3DOverlay() : - _dimensions(glm::vec3(DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE)) -{ -} +#include +#include Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : - Base3DOverlay(volume3DOverlay), - _dimensions(volume3DOverlay->_dimensions) + Base3DOverlay(volume3DOverlay) { } -Volume3DOverlay::~Volume3DOverlay() { +AABox Volume3DOverlay::getBounds() const { + auto extents = Extents{_localBoundingBox}; + extents.rotate(getRotation()); + extents.shiftBy(getPosition()); + + return AABox(extents); } void Volume3DOverlay::setProperties(const QScriptValue& properties) { @@ -58,26 +48,30 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) { QScriptValue z = dimensions.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - newDimensions.x = x.toVariant().toFloat(); - newDimensions.y = y.toVariant().toFloat(); - newDimensions.z = z.toVariant().toFloat(); + if (x.isValid() && x.isNumber() && + y.isValid() && y.isNumber() && + z.isValid() && z.isNumber()) { + newDimensions.x = x.toNumber(); + newDimensions.y = y.toNumber(); + newDimensions.z = z.toNumber(); validDimensions = true; } else { QScriptValue width = dimensions.property("width"); QScriptValue height = dimensions.property("height"); QScriptValue depth = dimensions.property("depth"); - if (width.isValid() && height.isValid() && depth.isValid()) { - newDimensions.x = width.toVariant().toFloat(); - newDimensions.y = height.toVariant().toFloat(); - newDimensions.z = depth.toVariant().toFloat(); + if (width.isValid() && width.isNumber() && + height.isValid() && height.isNumber() && + depth.isValid() && depth.isNumber()) { + newDimensions.x = width.toNumber(); + newDimensions.y = height.toNumber(); + newDimensions.z = depth.toNumber(); validDimensions = true; } } // size, scale, dimensions is special, it might just be a single scalar, check that here if (!validDimensions && dimensions.isNumber()) { - float size = dimensions.toVariant().toFloat(); + float size = dimensions.toNumber(); newDimensions.x = size; newDimensions.y = size; newDimensions.z = size; @@ -92,7 +86,7 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) { QScriptValue Volume3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { - return vec3toScriptValue(_scriptEngine, _dimensions); + return vec3toScriptValue(_scriptEngine, getDimensions()); } return Base3DOverlay::getProperty(property); @@ -100,24 +94,14 @@ QScriptValue Volume3DOverlay::getProperty(const QString& property) { bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { - // extents is the entity relative, scaled, centered extents of the entity - glm::vec3 position = getPosition(); - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(position); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 dimensions = _dimensions; - glm::vec3 corner = dimensions * -0.5f; // since we're going to do the ray picking in the overlay frame of reference - AABox overlayFrameBox(corner, dimensions); + glm::mat4 worldToEntityMatrix; + _transform.getInverseMatrix(worldToEntityMatrix); + glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame // and testing intersection there. - if (overlayFrameBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face)) { - return true; - } - return false; + return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face); } diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index b7c59b2ace..51f63dc580 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -14,36 +14,29 @@ // include this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" -#include - -#include - #include "Base3DOverlay.h" class Volume3DOverlay : public Base3DOverlay { Q_OBJECT public: - Volume3DOverlay(); + Volume3DOverlay() {} Volume3DOverlay(const Volume3DOverlay* volume3DOverlay); - ~Volume3DOverlay(); - - // getters - const glm::vec3& getCenter() const { return _position; } // TODO: consider adding registration point!! - glm::vec3 getCorner() const { return _position - (_dimensions * 0.5f); } // TODO: consider adding registration point!! - const glm::vec3& getDimensions() const { return _dimensions; } - - // setters - void setSize(float size) { _dimensions = glm::vec3(size, size, size); } - void setDimensions(const glm::vec3& value) { _dimensions = value; } + + virtual AABox getBounds() const; + + const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); } + void setDimensions(float value) { _localBoundingBox.setBox(glm::vec3(-value / 2.0f), value); } + void setDimensions(const glm::vec3& value) { _localBoundingBox.setBox(-value / 2.0f, value); } virtual void setProperties(const QScriptValue& properties); virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); - + protected: - glm::vec3 _dimensions; + // Centered local bounding box + AABox _localBoundingBox; }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5a39239948..a1bb9833bd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -795,6 +795,11 @@ void AudioClient::handleAudioInput() { delete[] inputAudioSamples; + // Remove DC offset + if (!_isStereoInput && !_audioSourceInjectEnabled) { + _inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples); + } + // only impose the noise gate and perform tone injection if we are sending mono audio if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) { _inputGate.gateSamples(networkAudioSamples, numNetworkSamples); diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 77f230809f..8766a20cdf 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -33,6 +33,32 @@ AudioNoiseGate::AudioNoiseGate() : } +void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { + // + // DC Offset correction + // + // Measure the DC offset over a trailing number of frames, and remove it from the input signal. + // This causes the noise background measurements and server muting to be more accurate. Many off-board + // ADC's have a noticeable DC offset. + // + const float DC_OFFSET_AVERAGING = 0.99f; + float measuredDcOffset = 0.0f; + // Remove trailing DC offset from samples + for (int i = 0; i < numSamples; i++) { + measuredDcOffset += samples[i]; + samples[i] -= (int16_t) _dcOffset; + } + // Update measured DC offset + measuredDcOffset /= numSamples; + if (_dcOffset == 0.0f) { + // On first frame, copy over measured offset + _dcOffset = measuredDcOffset; + } else { + _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset; + } +} + + void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { // // Impose Noise Gate @@ -61,17 +87,12 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { const int NOISE_GATE_WIDTH = 5; const int NOISE_GATE_CLOSE_FRAME_DELAY = 5; const int NOISE_GATE_FRAMES_TO_AVERAGE = 5; - const float DC_OFFSET_AVERAGING = 0.99f; - - // Check clipping, adjust DC offset, and check if should open noise gate - float measuredDcOffset = 0.0f; + + // Check clipping, and check if should open noise gate _didClipInLastFrame = false; for (int i = 0; i < numSamples; i++) { - measuredDcOffset += samples[i]; - samples[i] -= (int16_t) _dcOffset; thisSample = std::abs(samples[i]); - if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) { _didClipInLastFrame = true; } @@ -83,14 +104,6 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { } } - measuredDcOffset /= numSamples; - if (_dcOffset == 0.0f) { - // On first frame, copy over measured offset - _dcOffset = measuredDcOffset; - } else { - _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset; - } - _lastLoudness = fabs(loudness / numSamples); if (_quietestFrame > _lastLoudness) { diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio-client/src/AudioNoiseGate.h index bfb9ec28d5..8cb1155938 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio-client/src/AudioNoiseGate.h @@ -21,6 +21,7 @@ public: AudioNoiseGate(); void gateSamples(int16_t* samples, int numSamples); + void removeDCOffset(int16_t* samples, int numSamples); bool clippedInLastFrame() const { return _didClipInLastFrame; } float getMeasuredFloor() const { return _measuredFloor; } diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index c2d5c8aca9..c03f588d94 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -7,4 +7,10 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(networking shared) \ No newline at end of file +# we use libsoxr for resampling +add_dependency_external_projects(soxr) +find_package(Soxr REQUIRED) +target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS}) + +link_hifi_libraries(networking shared) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 675a3b8b28..36b0345d34 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -16,10 +16,12 @@ #include #include #include +#include #include "AbstractAudioInterface.h" #include "AudioRingBuffer.h" #include "AudioLogging.h" +#include "SoundCache.h" #include "AudioInjector.h" @@ -91,7 +93,6 @@ void AudioInjector::injectAudio() { } void AudioInjector::restart() { - qCDebug(audio) << "Restarting an AudioInjector by stopping and starting over."; connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished); if (!_isStarted || _isFinished) { emit finished(); @@ -284,3 +285,66 @@ void AudioInjector::stopAndDeleteLater() { stop(); QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); } + +AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) { + if (soundUrl.isEmpty()) { + return NULL; + } + auto soundCache = DependencyManager::get(); + if (soundCache.isNull()) { + return NULL; + } + SharedSoundPointer sound = soundCache.data()->getSound(QUrl(soundUrl)); + if (sound.isNull() || !sound->isReady()) { + return NULL; + } + + AudioInjectorOptions options; + options.stereo = sound->isStereo(); + options.position = position; + options.volume = volume; + + QByteArray samples = sound->getByteArray(); + if (stretchFactor == 1.0f) { + return playSound(samples, options, NULL); + } + + soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); + soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); + const int channelCount = sound->isStereo() ? 2 : 1; + const int standardRate = AudioConstants::SAMPLE_RATE; + const int resampledRate = standardRate * stretchFactor; + const int nInputSamples = samples.size() / sizeof(int16_t); + const int nOutputSamples = nInputSamples * stretchFactor; + QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0'); + const int16_t* receivedSamples = reinterpret_cast(samples.data()); + soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount, + receivedSamples, nInputSamples, NULL, + reinterpret_cast(resampled.data()), nOutputSamples, NULL, + &spec, &qualitySpec, 0); + if (soxError) { + qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate; + resampled = samples; + } + return playSound(resampled, options, NULL); +} + +AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { + QThread* injectorThread = new QThread(); + injectorThread->setObjectName("Audio Injector Thread"); + + AudioInjector* injector = new AudioInjector(buffer, options); + injector->setLocalAudioInterface(localInterface); + + injector->moveToThread(injectorThread); + + // start injecting when the injector thread starts + connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); + + // connect the right slots and signals for AudioInjector and thread cleanup + connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); + connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); + + injectorThread->start(); + return injector; +} diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 0513b70bd8..806a4ea33e 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -45,6 +45,10 @@ public: bool isLocalOnly() const { return _options.localOnly; } void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } + + static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); + static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position); + public slots: void injectAudio(); void restart(); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 19cc8d8412..40ca1ccbc9 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -17,10 +17,4 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) -# for changing the pitch of collision sounds -add_dependency_external_projects(soxr) -find_package(Soxr REQUIRED) -target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES}) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS}) - link_hifi_libraries(shared gpu script-engine render render-utils) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e1bd01547e..fe580eb137 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -27,10 +27,6 @@ #include #include #include -#include -#include -#include - #include "EntityTreeRenderer.h" @@ -48,16 +44,12 @@ #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" -#include "DependencyManager.h" -#include "AddressManager.h" - EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : OctreeRenderer(), _wantScripts(wantScripts), _entitiesScriptEngine(NULL), _sandboxScriptEngine(NULL), - _localAudioInterface(NULL), _lastMouseEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), @@ -1057,7 +1049,6 @@ void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) { } } - void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) { EntityItemPointer entity = entityTree->findEntityByEntityItemID(id); if (!entity) { @@ -1100,61 +1091,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { return; } - - auto soundCache = DependencyManager::get(); - if (soundCache.isNull()) { - return; - } - SharedSoundPointer sound = soundCache.data()->getSound(QUrl(collisionSoundURL)); - if (sound.isNull() || !sound->isReady()) { - return; - } - - // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all. + // Quiet sound aren't really heard at all, so we can compress everything to the range [1-c, 1], if we play it all. const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes. - float volume = energyFactorOfFull; - volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); - - // This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored. - AudioInjectorOptions options; - options.stereo = sound->isStereo(); - options.position = position; - options.volume = volume; + const float volume = (energyFactorOfFull * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); + // Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2) const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f; - QByteArray samples = sound->getByteArray(); - soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); - soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); - const int channelCount = sound->isStereo() ? 2 : 1; - const float factor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); - const int standardRate = AudioConstants::SAMPLE_RATE; - const int resampledRate = standardRate * factor; - const int nInputSamples = samples.size() / sizeof(int16_t); - const int nOutputSamples = nInputSamples * factor; - QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0'); - const int16_t* receivedSamples = reinterpret_cast(samples.data()); - soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount, - receivedSamples, nInputSamples, NULL, - reinterpret_cast(resampled.data()), nOutputSamples, NULL, - &spec, &qualitySpec, 0); - if (soxError) { - qCDebug(entitiesrenderer) << "Unable to resample" << collisionSoundURL << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate; - resampled = samples; - } - - AudioInjector* injector = new AudioInjector(resampled, options); - injector->setLocalAudioInterface(_localAudioInterface); - injector->triggerDeleteAfterFinish(); - QThread* injectorThread = new QThread(); - injectorThread->setObjectName("Audio Injector Thread"); - injector->moveToThread(injectorThread); - // start injecting when the injector thread starts - connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - // connect the right slots and signals for AudioInjector and thread cleanup - connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); - connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - injectorThread->start(); + const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); + AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position); } void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index e491524c78..56a8208eb4 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -158,7 +158,6 @@ private: QHash _entityScripts; void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision); - AbstractAudioInterface* _localAudioInterface; // So we can render collision sounds bool _lastMouseEventValid; MouseEvent _lastMouseEvent; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 65407c74e7..4b94992d59 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -43,9 +43,9 @@ void RenderableLineEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - // TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning. - //batch.setModelTransform(getTransformToCenter()); - batch.setModelTransform(Transform()); + Transform transform = Transform(); + transform.setTranslation(getPosition()); + batch.setModelTransform(transform); batch._glLineWidth(getLineWidth()); if (getLinePoints().size() > 1) { diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 547907625d..7f55005a17 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -14,7 +14,7 @@ #include - +#include #include #include #include diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 068bc98f7e..5f33da52f8 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -197,6 +197,8 @@ public: QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); } void setVoxelDataDirty() { _voxelDataChanged = true; } + + void setLinePointsDirty() {_linePointsChanged = true; } void setCreated(QDateTime& v); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a091c786b7..c9f7378bc8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -442,6 +442,43 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID, return true; } +bool EntityScriptingInterface::setPoints(QUuid entityID, std::function actor) { + if (!_entityTree) { + return false; + } + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType != EntityTypes::Line) { + return false; + } + + auto now = usecTimestampNow(); + + LineEntityItem* lineEntity = static_cast(entity.get()); + _entityTree->lockForWrite(); + bool success = actor(*lineEntity); + entity->setLastEdited(now); + entity->setLastBroadcast(now); + _entityTree->unlock(); + + _entityTree->lockForRead(); + EntityItemProperties properties = entity->getProperties(); + _entityTree->unlock(); + + properties.setLinePointsDirty(); + properties.setLastEdited(now); + + + queueEntityMessage(PacketTypeEntityEdit, entityID, properties); + return success; +} + bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) { polyVoxEntity.setSphere(center, radius, value); @@ -460,6 +497,21 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) { }); } +bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { + return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool + { + return lineEntity.setLinePoints(points); + }); +} + +bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) { + return setPoints(entityID, [point](LineEntityItem& lineEntity) -> bool + { + return lineEntity.appendPoint(point); + }); + +} + bool EntityScriptingInterface::actionWorker(const QUuid& entityID, std::function actor) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c6bc43c8c6..12c8688816 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -22,6 +22,7 @@ #include #include #include "PolyVoxEntityItem.h" +#include "LineEntityItem.h" #include "EntityEditPacketSender.h" @@ -121,6 +122,9 @@ public slots: Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); + + Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); + Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); Q_INVOKABLE void dumpTree() const; @@ -157,6 +161,7 @@ signals: private: bool actionWorker(const QUuid& entityID, std::function actor); bool setVoxels(QUuid entityID, std::function actor); + bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 094536f63a..fccf57c7c7 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -23,6 +23,7 @@ const float LineEntityItem::DEFAULT_LINE_WIDTH = 2.0f; +const int LineEntityItem::MAX_POINTS_PER_LINE = 70; EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -85,24 +86,38 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } -void LineEntityItem::setLinePoints(const QVector& points) { - QVector sanitizedPoints; - int invalidPoints = 0; +bool LineEntityItem::appendPoint(const glm::vec3& point) { + if (_points.size() > MAX_POINTS_PER_LINE - 1) { + qDebug() << "MAX POINTS REACHED!"; + return false; + } + glm::vec3 halfBox = getDimensions() * 0.5f; + if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { + qDebug() << "Point is outside entity's bounding box"; + return false; + } + _points << point; + _pointsChanged = true; + return true; +} + +bool LineEntityItem::setLinePoints(const QVector& points) { + if (points.size() > MAX_POINTS_PER_LINE) { + return false; + } for (int i = 0; i < points.size(); i++) { glm::vec3 point = points.at(i); - // Make sure all of our points are valid numbers. - // Must be greater than 0 because vector component is set to 0 if it is invalid data. Also should never be greater than TREE_SCALE - if ( (point.x > 0 && point.x < TREE_SCALE) && (point.y > 0 && point.y < TREE_SCALE) && (point.z > 0 && point.z < TREE_SCALE) ) { - sanitizedPoints << point; - } else { - ++invalidPoints; + glm::vec3 pos = getPosition(); + glm::vec3 halfBox = getDimensions() * 0.5f; + if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { + qDebug() << "Point is outside entity's bounding box"; + return false; } + } - if (invalidPoints > 0) { - qDebug() << "Line with" << invalidPoints << "INVALID POINTS"; - } - _points = sanitizedPoints; + _points = points; _pointsChanged = true; + return true; } int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 5151244b3c..6d0f00ef04 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -54,7 +54,8 @@ class LineEntityItem : public EntityItem { void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } - void setLinePoints(const QVector& points); + bool setLinePoints(const QVector& points); + bool appendPoint(const glm::vec3& point); const QVector& getLinePoints() const{ return _points; } @@ -68,6 +69,7 @@ class LineEntityItem : public EntityItem { virtual void debugDump() const; static const float DEFAULT_LINE_WIDTH; + static const int MAX_POINTS_PER_LINE; protected: rgbColor _color; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 5ef101225c..9004c4a8fe 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -12,6 +12,8 @@ #include "GLBackendShared.h" #include +using namespace gpu; + GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { (&::gpu::GLBackend::do_draw), @@ -142,6 +144,14 @@ bool GLBackend::checkGLError(const char* name) { } } +bool GLBackend::checkGLErrorDebug(const char* name) { +#ifdef DEBUG + return checkGLError(name); +#else + Q_UNUSED(name); + return false; +#endif +} void GLBackend::syncCache() { syncTransformStateCache(); @@ -687,4 +697,20 @@ void GLBackend::fetchMatrix(GLenum target, glm::mat4 & m) { glGetFloatv(target, glm::value_ptr(m)); } +void GLBackend::checkGLStackStable(std::function f) { +#ifdef DEBUG + GLint mvDepth, prDepth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepth); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepth); +#endif + f(); + +#ifdef DEBUG + GLint mvDepthFinal, prDepthFinal; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &mvDepthFinal); + glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &prDepthFinal); + Q_ASSERT(mvDepth == mvDepthFinal); + Q_ASSERT(prDepth == prDepthFinal); +#endif +} diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f20b6735b0..28236c68c9 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -12,11 +12,13 @@ #define hifi_gpu_GLBackend_h #include +#include +#include + #include "GPUConfig.h" #include "Context.h" #include "Batch.h" -#include namespace gpu { @@ -47,6 +49,11 @@ public: static bool checkGLError(const char* name = nullptr); + // Only checks in debug builds + static bool checkGLErrorDebug(const char* name = nullptr); + + static void checkGLStackStable(std::function f); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index b0dee550d9..903c97f45b 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -11,6 +11,7 @@ #include "GPULogging.h" #include "GLBackendShared.h" +using namespace gpu; GLBackend::GLFramebuffer::GLFramebuffer() {} diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 92e06e1d11..365934379f 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -17,9 +17,7 @@ #include "Batch.h" -using namespace gpu; - -static const GLenum _primitiveToGLmode[NUM_PRIMITIVES] = { +static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, @@ -30,7 +28,7 @@ static const GLenum _primitiveToGLmode[NUM_PRIMITIVES] = { GL_QUAD_STRIP, }; -static const GLenum _elementTypeToGLType[NUM_TYPES]= { +static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { GL_FLOAT, GL_INT, GL_UNSIGNED_INT, @@ -49,10 +47,12 @@ static const GLenum _elementTypeToGLType[NUM_TYPES]= { GL_UNSIGNED_BYTE }; -#if _DEBUG -#define CHECK_GL_ERROR() ::gpu::GLBackend::checkGLError(__FUNCTION__) -#else -#define CHECK_GL_ERROR() false -#endif +// Stupid preprocessor trick to turn the line macro into a string +#define CHECK_GL_ERROR_HELPER(x) #x +// FIXME doesn't build on Linux or Mac. Hmmmm +// #define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__ ":" CHECK_GL_ERROR_HELPER(__LINE__)) +#define CHECK_GL_ERROR() gpu::GLBackend::checkGLErrorDebug(__FUNCTION__) + +#define CHECK_GL_STACK_STABLE(f) gpu::GLBackend::checkGLStackStable(f) #endif diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 05e1e8fed8..72c7de8504 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -11,6 +11,7 @@ #include "GPULogging.h" #include "GLBackendShared.h" +using namespace gpu; GLBackend::GLTexture::GLTexture() : _storageStamp(0), diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index a8ed54fdd1..591b844aed 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -308,6 +308,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const handlePath(returnedPath, trigger); } } else { + // we're going to hit the index path, set that as the _newHostLookupPath + _newHostLookupPath = INDEX_PATH; + // we didn't override the path or get one back - ask the DS for the viewpoint of its index path // which we will jump to if it exists emit pathChangeRequired(INDEX_PATH); @@ -479,6 +482,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should // We use _newHostLookupPath to determine if the client has already stored its last address // before moving to a new host thanks to the information in the same lookup URL. + if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) { addCurrentAddressToHistory(LookupTrigger::UserInput); } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index a80d071851..bb6f62ef84 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -350,6 +350,19 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { if (rule.extensionFormat.contains("%N")) { if (rule.maxBackupVersions > 0) { qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "..."; + + // Delete maximum rolling file because rename() fails on Windows if target exists + QString backupMaxExtensionN = rule.extensionFormat; + backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions)); + QString backupMaxFilenameN = _filename + backupMaxExtensionN; + QFile backupMaxFileN(backupMaxFilenameN); + if (backupMaxFileN.exists()) { + int result = remove(qPrintable(backupMaxFilenameN)); + if (result != 0) { + qCDebug(octree) << "ERROR deleting old backup file " << backupMaxFilenameN; + } + } + for(int n = rule.maxBackupVersions - 1; n > 0; n--) { QString backupExtensionN = rule.extensionFormat; QString backupExtensionNplusOne = rule.extensionFormat; diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 815376a456..820ab32b67 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -65,9 +65,10 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { _farClip = -_corners[BOTTOM_LEFT_FAR].z; _aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) / (_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y); - glm::vec3 right = glm::normalize(glm::vec3(_corners[TOP_RIGHT_NEAR])); - glm::vec3 left = glm::normalize(glm::vec3(_corners[TOP_LEFT_NEAR])); - _fieldOfView = abs(glm::degrees(glm::angle(right, left))); + + glm::vec4 top = _inverseProjection * vec4(0, 1, -1, 1); + top /= top.w; + _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0, 0, -1), glm::normalize(vec3(top)))))); } // ViewFrustum::calculateViewFrustum() diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index cd1e010818..3e41b58bb6 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -30,8 +30,7 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f; const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f; const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; -//const float DEFAULT_NEAR_CLIP = 0.08f; -const float DEFAULT_NEAR_CLIP = 0.25f; +const float DEFAULT_NEAR_CLIP = 0.08f; const float DEFAULT_FAR_CLIP = (float)TREE_SCALE; class ViewFrustum { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index a6602269a4..3e7e9a0adf 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -28,6 +28,9 @@ #include "RenderUtilsLogging.h" #include "GeometryCache.h" +#include "standardTransformPNTC_vert.h" +#include "standardDrawTexture_frag.h" + //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; @@ -1182,6 +1185,26 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co batch.draw(gpu::QUADS, 4, 0); } +void GeometryCache::renderUnitCube(gpu::Batch& batch) { + static const glm::vec4 color(1); + renderSolidCube(batch, 1, color); +} + +void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) { + gpu::Batch batch; + renderUnitQuad(batch, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) { + static const glm::vec2 topLeft(-1, 1); + static const glm::vec2 bottomRight(1, -1); + static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); + static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); + renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); +} + + void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { @@ -1805,6 +1828,23 @@ QSharedPointer GeometryCache::createResource(const QUrl& url, const QS return geometry.staticCast(); } +void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) { + if (!_standardDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); + } + batch.setPipeline(_standardDrawPipeline); +} + const float NetworkGeometry::NO_HYSTERESIS = -1.0f; NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index b438eb2d3b..f97ab8a773 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,6 +155,10 @@ public: void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); + void renderUnitCube(gpu::Batch& batch); + void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) @@ -249,6 +253,9 @@ public: /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + /// Set a batch to the simple pipeline, returning the previous pipeline + void useSimpleDrawPipeline(gpu::Batch& batch); + protected: virtual QSharedPointer createResource(const QUrl& url, @@ -266,6 +273,7 @@ private: int vertexSize; }; + gpu::PipelinePointer _standardDrawPipeline; QHash _cubeVerticies; QHash _cubeColors; gpu::BufferPointer _wireCubeIndexBuffer; @@ -273,7 +281,7 @@ private: QHash _solidCubeVertices; QHash _solidCubeColors; gpu::BufferPointer _solidCubeIndexBuffer; - + class BatchItemDetails { public: static int population; diff --git a/libraries/render-utils/src/JointState.h b/libraries/render-utils/src/JointState.h index fbe2e9c986..0ef84e50c4 100644 --- a/libraries/render-utils/src/JointState.h +++ b/libraries/render-utils/src/JointState.h @@ -106,6 +106,9 @@ public: glm::quat computeParentRotation() const; glm::quat computeVisibleParentRotation() const; + void setTransform(const glm::mat4& transform) { _transform = transform; } + void setVisibleTransform(const glm::mat4& transform) { _visibleTransform = transform; } + private: void setRotationInConstrainedFrameInternal(const glm::quat& targetRotation); /// debug helper function diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1b991f2179..93f3f345f0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -545,11 +545,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's ray intersection by mapping our origin and direction into the model frame // and testing intersection there. if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) { - - if (!_calculatedMeshBoxesValid) { - recalculateMeshBoxes(pickAgainstTriangles); - } - float bestDistance = std::numeric_limits::max(); float distanceToSubMesh; @@ -560,6 +555,11 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // If we hit the models box, then consider the submeshes... _mutex.lock(); + + if (!_calculatedMeshBoxesValid) { + recalculateMeshBoxes(pickAgainstTriangles); + } + foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) { @@ -1102,9 +1102,11 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo _readyWhenAdded = false; // reset out render items. _needsReload = true; invalidCalculatedMeshBoxes(); - + _url = url; + onInvalidate(); + // if so instructed, keep the current geometry until the new one is loaded _nextBaseGeometry = _nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; @@ -1368,6 +1370,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { // because ray picking needs valid boxes to work _calculatedMeshBoxesValid = false; _calculatedMeshTrianglesValid = false; + onInvalidate(); // check for scale to fit if (_scaleToFit && !_scaledToFit) { @@ -1380,6 +1383,26 @@ void Model::simulate(float deltaTime, bool fullUpdate) { } } +void Model::updateClusterMatrices() { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 modelToWorld = glm::mat4_cast(_rotation); + for (int i = 0; i < _meshStates.size(); i++) { + MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + if (_showTrueJointTransforms) { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + } + } else { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + } + } + } +} + void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints @@ -1811,7 +1834,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // We need to make sure we have valid offsets calculated before we can render if (!_calculatedMeshPartOffsetValid) { + _mutex.lock(); recalculateMeshPartOffsets(); + _mutex.unlock(); } auto textureCache = DependencyManager::get(); @@ -2019,7 +2044,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } } + _mutex.lock(); qint64 offset = _calculatedMeshPartOffset[QPair(meshIndex, partIndex)]; + _mutex.unlock(); if (part.quadIndices.size() > 0) { batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); @@ -2116,6 +2143,34 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f } } +bool Model::initWhenReady(render::ScenePointer scene) { + if (isActive() && isRenderable() && !_meshGroupsKnown && isLoadedWithTextures()) { + segregateMeshGroups(); + + render::PendingChanges pendingChanges; + + foreach (auto renderItem, _transparentRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + _renderItems.insert(item, renderPayload); + pendingChanges.resetItem(item, renderPayload); + } + + foreach (auto renderItem, _opaqueRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + _renderItems.insert(item, renderPayload); + pendingChanges.resetItem(item, renderPayload); + } + scene->enqueuePendingChanges(pendingChanges); + + _readyWhenAdded = true; + return true; + } + return false; +} ModelBlender::ModelBlender() : _pendingBlenders(0) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 6dfe223581..3748403b97 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -240,6 +240,8 @@ public: AABox getPartBounds(int meshIndex, int partIndex); void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); + bool initWhenReady(render::ScenePointer scene); + protected: QSharedPointer _geometry; @@ -312,6 +314,12 @@ protected: _calculatedMeshTrianglesValid = false; } + // rebuild the clusterMatrices from the current jointStates + void updateClusterMatrices(); + + // hook for derived classes to be notified when setUrl invalidates the current model. + virtual void onInvalidate() {}; + private: friend class AnimationHandle; diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 9b497964e7..b6bc6b721e 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -11,6 +11,9 @@ #include #include #include + +#include + #include "AbstractViewStateInterface.h" Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) @@ -91,6 +94,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { // Qt bug in 5.4 forces this check of pixel ratio, // even though we're rendering offscreen. qreal pixelRatio = 1.0; + _qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize); if (_renderControl && _renderControl->_renderWindow) { pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); } else { @@ -111,7 +115,6 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { _quickWindow->contentItem()->setSize(newSize); } - // Update our members if (_rootItem) { _rootItem->setSize(newSize); @@ -212,6 +215,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function_batch = nullptr; args->_whiteTexture.reset(); } - diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 2fc30d7267..2f65c5ade6 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -53,10 +53,10 @@ public: }; class DrawOverlay3D { - mutable gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable + static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable public: - const gpu::PipelinePointer& getOpaquePipeline() const; - + static const gpu::PipelinePointer& getOpaquePipeline(); + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); typedef render::Job::Model JobModel; diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf new file mode 100644 index 0000000000..4fbeb6eb7f --- /dev/null +++ b/libraries/render-utils/src/standardDrawTexture.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// standardDrawTexture.frag +// fragment shader +// +// Created by Sam Gateau on 6/10/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 +// + +// the texture +uniform sampler2D colorMap; + +varying vec2 varTexcoord; +varying vec4 varColor; + + +void main(void) { + vec4 color = texture2D(colorMap, varTexcoord); + gl_FragColor = color * varColor; +} diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv new file mode 100644 index 0000000000..93abea267c --- /dev/null +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -0,0 +1,35 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// standardTransformPNTC.slv +// vertex shader +// +// Created by Sam Gateau on 6/10/2015. +// 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()$> + +varying vec3 varPosition; +varying vec3 varNormal; +varying vec2 varTexcoord; +varying vec4 varColor; + +void main(void) { + varTexcoord = gl_MultiTexCoord0.xy; + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$> + varNormal = normalize(varNormal); + varPosition = gl_Vertex.xyz; +} \ No newline at end of file diff --git a/libraries/render-utils/src/stars.slf b/libraries/render-utils/src/stars.slf new file mode 100644 index 0000000000..f0f81cb5f4 --- /dev/null +++ b/libraries/render-utils/src/stars.slf @@ -0,0 +1,76 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// stars.frag +// fragment shader +// +// Created by Bradley Austin Davis on 2015/06/19 + +varying vec2 varTexcoord; +varying vec3 varNomral; +varying vec3 varPosition; + + +const int star_iterations = 14; +const float time_scale = 0.2; +const vec3 col_star = vec3( 1.0, 0.7, 0.5 ); + +float hash( float n ) { return fract(sin(n)*123.456789); } + +vec2 rotate( in vec2 uv, float a) +{ + float c = cos( a ); + float s = sin( a ); + return vec2( c * uv.x - s * uv.y, s * uv.x + c * uv.y ); +} + +float noise( in vec3 p ) +{ + vec3 fl = floor( p ); + vec3 fr = fract( p ); + fr = fr * fr * ( 3.0 - 2.0 * fr ); + + float n = fl.x + fl.y * 157.0 + 113.0 * fl.z; + return mix( mix( mix( hash( n + 0.0), hash( n + 1.0 ), fr.x ), + mix( hash( n + 157.0), hash( n + 158.0 ), fr.x ), fr.y ), + mix( mix( hash( n + 113.0), hash( n + 114.0 ), fr.x ), + mix( hash( n + 270.0), hash( n + 271.0 ), fr.x ), fr.y ), fr.z ); +} + +float fbm( in vec2 p, float t ) +{ + float f; + f = 0.5000 * noise( vec3( p, t ) ); p *= 2.1; + f += 0.2500 * noise( vec3( p, t ) ); p *= 2.2; + f += 0.1250 * noise( vec3( p, t ) ); p *= 2.3; + f += 0.0625 * noise( vec3( p, t ) ); + return f; +} + +vec3 doBackgroundStars( in vec3 dir ) +{ + vec3 n = abs( dir ); + vec2 uv = ( n.x > n.y && n.x > n.z ) ? dir.yz / dir.x: + ( n.y > n.x && n.y > n.z ) ? dir.zx / dir.y: + dir.xy / dir.z; + + float f = 0.0; + + for( int i = 0 ; i < star_iterations; ++i ) + { + uv = rotate( 1.07 * uv + vec2( 0.7 ), 0.5 ); + + float t = 10. * uv.x * uv.y; + vec2 u = cos( 100. * uv ) * fbm( 10. * uv, 0.0 ); + f += smoothstep( 0.5, 0.55, u.x * u.y ) * ( 0.25 * sin( t ) + 0.75 ); + } + + return f * col_star; +} + +void main(void) { + vec3 c = doBackgroundStars( normalize(varPosition) ); + c = pow( c, vec3( 0.4545 ) ); + gl_FragColor = vec4( c, 1.0 ); +} + diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index f0ae3bbacb..b44e13f012 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -83,6 +83,9 @@ public slots: virtual void captureWheelEvents() = 0; virtual void releaseWheelEvents() = 0; + + virtual void captureActionEvents() = 0; + virtual void releaseActionEvents() = 0; virtual void captureJoystick(int joystickIndex) = 0; virtual void releaseJoystick(int joystickIndex) = 0; @@ -112,6 +115,8 @@ signals: void touchUpdateEvent(const TouchEvent& event); void wheelEvent(const WheelEvent& event); + + void actionEvent(int action, float state); }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 9e3e924933..161c7b7118 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -46,24 +46,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi AudioInjectorOptions optionsCopy = injectorOptions; optionsCopy.stereo = sound->isStereo(); - QThread* injectorThread = new QThread(); - injectorThread->setObjectName("Audio Injector Thread"); - - AudioInjector* injector = new AudioInjector(sound, optionsCopy); - injector->setLocalAudioInterface(_localAudioInterface); - - injector->moveToThread(injectorThread); - - // start injecting when the injector thread starts - connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - - // connect the right slots and signals for AudioInjector and thread cleanup - connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); - connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - - injectorThread->start(); - - return new ScriptAudioInjector(injector); + return new ScriptAudioInjector(AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface)); } else { qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object."; diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index b89a274af1..92bc5d31da 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -28,7 +28,7 @@ public slots: void restart() { _injector->restart(); } void stop() { _injector->stop(); } - void setOptions(AudioInjectorOptions& options) { _injector->setOptions(options); } + void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } float getLoudness() const { return _injector->getLoudness(); } bool isPlaying() const { return _injector->isPlaying(); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 182a0aea8d..0945a556f2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -317,7 +317,10 @@ void ScriptEngine::init() { registerAnimationTypes(this); registerAvatarTypes(this); registerAudioMetaTypes(this); - _controllerScriptingInterface->registerControllerTypes(this); + + if (_controllerScriptingInterface) { + _controllerScriptingInterface->registerControllerTypes(this); + } qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); diff --git a/libraries/shared/src/Extents.cpp b/libraries/shared/src/Extents.cpp index f48ba3c99f..ad00683cf2 100644 --- a/libraries/shared/src/Extents.cpp +++ b/libraries/shared/src/Extents.cpp @@ -10,12 +10,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Extents.h" + #include #include #include #include "AABox.h" -#include "Extents.h" +#include "Transform.h" void Extents::reset() { minimum = glm::vec3(FLT_MAX); @@ -78,3 +80,10 @@ void Extents::rotate(const glm::quat& rotation) { glm::max(topRightNearRotated, glm::max(topLeftFarRotated,topRightFarRotated))))))); } + + +void Extents::transform(const Transform& transform) { + scale(transform.getScale()); + rotate(transform.getRotation()); + shiftBy(transform.getTranslation()); +} diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 647f33699a..3d5a2dbcec 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -20,6 +20,7 @@ #include "StreamUtils.h" class AABox; +class Transform; class Extents { public: @@ -56,6 +57,13 @@ public: /// rotate the extents around orign by rotation void rotate(const glm::quat& rotation); + + /// scale the extents around orign by scale + void scale(float scale) { minimum *= scale; maximum *= scale; } + void scale(const glm::vec3& scale) { minimum *= scale; maximum *= scale; } + + // Transform the extents with transform + void transform(const Transform& transform); glm::vec3 size() const { return maximum - minimum; } float largestDimension() const {glm::vec3 s = size(); return glm::max(s[0], s[1], s[2]); } diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 969df8d5ee..5d2742feae 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -131,6 +131,21 @@ float aspect(const T& t) { return (float)t.x / (float)t.y; } +// Take values in an arbitrary range [0, size] and convert them to the range [0, 1] +template +T toUnitScale(const T& value, const T& size) { + return value / size; +} + +// Take values in an arbitrary range [0, size] and convert them to the range [0, 1] +template +T toNormalizedDeviceScale(const T& value, const T& size) { + vec2 result = toUnitScale(value, size); + result *= 2.0f; + result -= 1.0f; + return result; +} + #define YAW(euler) euler.y #define PITCH(euler) euler.x #define ROLL(euler) euler.z diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 241f835a46..50f87fe258 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -20,6 +20,7 @@ static int vec4MetaTypeId = qRegisterMetaType(); static int vec3MetaTypeId = qRegisterMetaType(); +static int qVectorVec3MetaTypeId = qRegisterMetaType>(); static int vec2MetaTypeId = qRegisterMetaType(); static int quatMetaTypeId = qRegisterMetaType(); static int xColorMetaTypeId = qRegisterMetaType(); @@ -31,6 +32,7 @@ static int collisionMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); @@ -93,6 +95,16 @@ QVector qVectorVec3FromScriptValue(const QScriptValue& array){ return newVector; } +void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector ) { + int length = array.property("length").toInteger(); + + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + vector << newVec3; + } +} + QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec2.x); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 1dcc85107a..f5f39cd3f6 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -57,6 +57,7 @@ QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); void qURLFromScriptValue(const QScriptValue& object, QUrl& url); QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorVec3FromScriptValue( const QScriptValue& array); class PickRay { diff --git a/libraries/ui/src/Tooltip.cpp b/libraries/ui/src/Tooltip.cpp new file mode 100644 index 0000000000..35b17b74a2 --- /dev/null +++ b/libraries/ui/src/Tooltip.cpp @@ -0,0 +1,51 @@ +// +// Tooltip.cpp +// +// Created by Bradley Austin Davis on 2015/04/14 +// 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 "Tooltip.h" +#include + +HIFI_QML_DEF(Tooltip) + +Tooltip::Tooltip(QQuickItem* parent) : QQuickItem(parent) { +} + +Tooltip::~Tooltip() { +} + +QString Tooltip::text() const { + return _text; +} + +void Tooltip::setText(const QString& arg) { + if (arg != _text) { + _text = arg; + emit textChanged(); + } +} + +void Tooltip::setVisible(bool visible) { + QQuickItem::setVisible(visible); +} + +QString Tooltip::showTip(const QString& text) { + const QString newTipId = QUuid().createUuid().toString(); + Tooltip::show([&](QQmlContext*, QObject* object) { + object->setObjectName(newTipId); + object->setProperty("text", text); + }); + return newTipId; +} + +void Tooltip::closeTip(const QString& tipId) { + auto rootItem = DependencyManager::get()->getRootItem(); + QQuickItem* that = rootItem->findChild(tipId); + if (that) { + that->deleteLater(); + } +} \ No newline at end of file diff --git a/libraries/ui/src/Tooltip.h b/libraries/ui/src/Tooltip.h new file mode 100644 index 0000000000..7292fe9399 --- /dev/null +++ b/libraries/ui/src/Tooltip.h @@ -0,0 +1,45 @@ +// +// Tooltip.h +// +// Created by Bradley Austin Davis on 2015/04/14 +// 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 +#ifndef hifi_Tooltip_h +#define hifi_Tooltip_h + +#include "OffscreenQmlDialog.h" + +class Tooltip : public QQuickItem +{ + Q_OBJECT + HIFI_QML_DECL + +private: + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + Tooltip(QQuickItem* parent = 0); + virtual ~Tooltip(); + + QString text() const; + + static QString showTip(const QString& text); + static void closeTip(const QString& tipId); + +public slots: + virtual void setVisible(bool v); + void setText(const QString& arg); + +signals: + void textChanged(); + +private: + QString _text; +}; + +#endif // hifi_Tooltip_h