mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 04:57:58 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into orange
This commit is contained in:
commit
377ee5a486
51 changed files with 687 additions and 727 deletions
|
@ -743,7 +743,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
this.searchSphereOff();
|
this.searchSphereOff();
|
||||||
|
|
||||||
Controller.setReticleVisible(true);
|
Reticle.setVisible(true);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1026,7 +1026,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.searchIndicatorOn(handPosition, distantPickRay);
|
this.searchIndicatorOn(handPosition, distantPickRay);
|
||||||
Controller.setReticleVisible(false);
|
Reticle.setVisible(false);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1892,7 +1892,7 @@ function cleanup() {
|
||||||
rightController.cleanup();
|
rightController.cleanup();
|
||||||
leftController.cleanup();
|
leftController.cleanup();
|
||||||
Controller.disableMapping(MAPPING_NAME);
|
Controller.disableMapping(MAPPING_NAME);
|
||||||
Controller.setReticleVisible(true);
|
Reticle.setVisible(true);
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(cleanup);
|
Script.scriptEnding.connect(cleanup);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
|
|
|
@ -24,9 +24,9 @@ var PITCH_SCALING = 10.0;
|
||||||
var YAW_SCALING = 10.0;
|
var YAW_SCALING = 10.0;
|
||||||
|
|
||||||
var EXPECTED_CHANGE = 50;
|
var EXPECTED_CHANGE = 50;
|
||||||
var lastPos = Controller.getReticlePosition();
|
var lastPos = Reticle.getPosition();
|
||||||
function moveReticle(dY, dX) {
|
function moveReticle(dY, dX) {
|
||||||
var globalPos = Controller.getReticlePosition();
|
var globalPos = Reticle.getPosition();
|
||||||
|
|
||||||
// some debugging to see if position is jumping around on us...
|
// some debugging to see if position is jumping around on us...
|
||||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||||
|
@ -45,7 +45,7 @@ function moveReticle(dY, dX) {
|
||||||
|
|
||||||
globalPos.x += dX;
|
globalPos.x += dX;
|
||||||
globalPos.y += dY;
|
globalPos.y += dY;
|
||||||
Controller.setReticlePosition(globalPos);
|
Reticle.setPosition(globalPos);
|
||||||
lastPos = globalPos;
|
lastPos = globalPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,9 @@ function length(posA, posB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var EXPECTED_CHANGE = 50;
|
var EXPECTED_CHANGE = 50;
|
||||||
var lastPos = Controller.getReticlePosition();
|
var lastPos = Reticle.getPosition();
|
||||||
function moveReticle(dX, dY) {
|
function moveReticle(dX, dY) {
|
||||||
var globalPos = Controller.getReticlePosition();
|
var globalPos = Reticle.getPosition();
|
||||||
|
|
||||||
// some debugging to see if position is jumping around on us...
|
// some debugging to see if position is jumping around on us...
|
||||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||||
|
@ -52,7 +52,7 @@ function moveReticle(dX, dY) {
|
||||||
|
|
||||||
globalPos.x += dX;
|
globalPos.x += dX;
|
||||||
globalPos.y += dY;
|
globalPos.y += dY;
|
||||||
Controller.setReticlePosition(globalPos);
|
Reticle.setPosition(globalPos);
|
||||||
lastPos = globalPos;
|
lastPos = globalPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,10 @@ function length(posA, posB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveReticleAbsolute(x, y) {
|
function moveReticleAbsolute(x, y) {
|
||||||
var globalPos = Controller.getReticlePosition();
|
var globalPos = Reticle.getPosition();
|
||||||
globalPos.x = x;
|
globalPos.x = x;
|
||||||
globalPos.y = y;
|
globalPos.y = y;
|
||||||
Controller.setReticlePosition(globalPos);
|
Reticle.setPosition(globalPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
||||||
|
@ -53,8 +53,9 @@ Script.update.connect(function(deltaTime) {
|
||||||
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
|
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
|
||||||
|
|
||||||
// NOTE: hack for now
|
// NOTE: hack for now
|
||||||
var screenSizeX = 1920;
|
var screenSize = Reticle.maximumPosition;
|
||||||
var screenSizeY = 1080;
|
var screenSizeX = screenSize.x;
|
||||||
|
var screenSizeY = screenSize.y;
|
||||||
|
|
||||||
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
||||||
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
||||||
|
|
|
@ -158,10 +158,7 @@ Mouse.prototype.startRotateDrag = function() {
|
||||||
x: this.current.x,
|
x: this.current.x,
|
||||||
y: this.current.y
|
y: this.current.y
|
||||||
};
|
};
|
||||||
this.cursorRestore = {
|
this.cursorRestore = Reticle.getPosition();
|
||||||
x: Window.getCursorPositionX(),
|
|
||||||
y: Window.getCursorPositionY()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mouse.prototype.getDrag = function() {
|
Mouse.prototype.getDrag = function() {
|
||||||
|
@ -177,7 +174,7 @@ Mouse.prototype.getDrag = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mouse.prototype.restoreRotateCursor = function() {
|
Mouse.prototype.restoreRotateCursor = function() {
|
||||||
Window.setCursorPosition(this.cursorRestore.x, this.cursorRestore.y);
|
Reticle.setPosition(this.cursorRestore);
|
||||||
this.current = {
|
this.current = {
|
||||||
x: this.rotateStart.x,
|
x: this.rotateStart.x,
|
||||||
y: this.rotateStart.y
|
y: this.rotateStart.y
|
||||||
|
|
|
@ -54,8 +54,8 @@ var velocity = { x: 0, y: 0, z: 0 };
|
||||||
var velocityVertical = 0;
|
var velocityVertical = 0;
|
||||||
var enabled = false;
|
var enabled = false;
|
||||||
|
|
||||||
var lastX = Window.getCursorPositionX();
|
var lastX = Reticle.getPosition().x;
|
||||||
var lastY = Window.getCursorPositionY();
|
var lastY = Reticle.getPosition().y;
|
||||||
var yawFromMouse = 0;
|
var yawFromMouse = 0;
|
||||||
var pitchFromMouse = 0;
|
var pitchFromMouse = 0;
|
||||||
|
|
||||||
|
@ -109,8 +109,8 @@ function update(dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled && Window.hasFocus()) {
|
if (enabled && Window.hasFocus()) {
|
||||||
var x = Window.getCursorPositionX();
|
var x = Reticle.getPosition().x;
|
||||||
var y = Window.getCursorPositionY();
|
var y = Reticle.getPosition().y;
|
||||||
|
|
||||||
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||||
|
@ -173,7 +173,7 @@ function scriptEnding() {
|
||||||
function resetCursorPosition() {
|
function resetCursorPosition() {
|
||||||
var newX = Window.x + Window.innerWidth / 2;
|
var newX = Window.x + Window.innerWidth / 2;
|
||||||
var newY = Window.y + Window.innerHeight / 2;
|
var newY = Window.y + Window.innerHeight / 2;
|
||||||
Window.setCursorPosition(newX, newY);
|
Reticle.setPosition({ x: newX, y: newY});
|
||||||
lastX = newX;
|
lastX = newX;
|
||||||
lastY = newY;
|
lastY = newY;
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ function enable() {
|
||||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||||
Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] });
|
Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||||
}
|
}
|
||||||
Window.setCursorVisible(false);
|
Reticle.setVisible(false);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ function disable() {
|
||||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||||
Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] });
|
Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||||
}
|
}
|
||||||
Window.setCursorVisible(true);
|
Reticle.setVisible(true);
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,8 +281,8 @@ CameraManager = function() {
|
||||||
|
|
||||||
that.mouseMoveEvent = function(event) {
|
that.mouseMoveEvent = function(event) {
|
||||||
if (that.enabled && that.mode != MODE_INACTIVE) {
|
if (that.enabled && that.mode != MODE_INACTIVE) {
|
||||||
var x = Window.getCursorPositionX();
|
var x = Reticle.getPosition().x;
|
||||||
var y = Window.getCursorPositionY();
|
var y = Reticle.getPosition().y;
|
||||||
if (!hasDragged) {
|
if (!hasDragged) {
|
||||||
that.lastMousePosition.x = x;
|
that.lastMousePosition.x = x;
|
||||||
that.lastMousePosition.y = y;
|
that.lastMousePosition.y = y;
|
||||||
|
@ -337,7 +337,7 @@ CameraManager = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatePosition) {
|
if (updatePosition) {
|
||||||
Window.setCursorPosition(newX, newY);
|
Reticle.setPosition({ x: newX, y: newY});
|
||||||
}
|
}
|
||||||
|
|
||||||
that.lastMousePosition.x = newX;
|
that.lastMousePosition.x = newX;
|
||||||
|
@ -384,7 +384,7 @@ CameraManager = function() {
|
||||||
if (!that.enabled) return;
|
if (!that.enabled) return;
|
||||||
|
|
||||||
that.mode = MODE_INACTIVE;
|
that.mode = MODE_INACTIVE;
|
||||||
Window.setCursorVisible(true);
|
Reticle.setVisible(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
var lastX = Window.getCursorPositionX();
|
var lastX = Reticle.getPosition().x;
|
||||||
var lastY = Window.getCursorPositionY();
|
var lastY = Reticle.getPosition().y;
|
||||||
var yawFromMouse = 0;
|
var yawFromMouse = 0;
|
||||||
var pitchFromMouse = 0;
|
var pitchFromMouse = 0;
|
||||||
|
|
||||||
|
@ -121,9 +121,9 @@ var mouseLook = (function () {
|
||||||
|
|
||||||
function onScriptUpdate(dt) {
|
function onScriptUpdate(dt) {
|
||||||
if (active && Window.hasFocus()) {
|
if (active && Window.hasFocus()) {
|
||||||
var x = Window.getCursorPositionX();
|
var x = Reticle.getPosition().x;
|
||||||
// I'm not sure why this + 0.5 is necessary?
|
// I'm not sure why this + 0.5 is necessary?
|
||||||
var y = Window.getCursorPositionY() + 0.5;
|
var y = Reticle.getPosition().y; + 0.5;
|
||||||
|
|
||||||
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||||
|
@ -155,7 +155,7 @@ var mouseLook = (function () {
|
||||||
function resetCursorPosition() {
|
function resetCursorPosition() {
|
||||||
var newX = Window.x + Window.innerWidth / 2.0;
|
var newX = Window.x + Window.innerWidth / 2.0;
|
||||||
var newY = Window.y + Window.innerHeight / 2.0;
|
var newY = Window.y + Window.innerHeight / 2.0;
|
||||||
Window.setCursorPosition(newX, newY);
|
Reticle.setPosition({ x: newX, y: newY});
|
||||||
lastX = newX;
|
lastX = newX;
|
||||||
lastY = newY;
|
lastY = newY;
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,15 +267,9 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
color: root.fontColor;
|
color: root.fontColor;
|
||||||
font.pixelSize: root.fontSize
|
font.pixelSize: root.fontSize
|
||||||
visible: root.showAcuity
|
visible: root.expanded
|
||||||
text: "LOD: " + root.lodStatus;
|
text: "LOD: " + root.lodStatus;
|
||||||
}
|
}
|
||||||
Text {
|
|
||||||
color: root.fontColor;
|
|
||||||
font.pixelSize: root.fontSize
|
|
||||||
visible: root.expanded && !root.showAcuity
|
|
||||||
text: root.lodStatsRenderText;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
|
||||||
|
|
||||||
static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
|
static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
|
||||||
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
|
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
|
||||||
static const unsigned int CAPPED_SIM_FRAMERATE = 60;
|
static const unsigned int CAPPED_SIM_FRAMERATE = 120;
|
||||||
static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE;
|
static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE;
|
||||||
|
|
||||||
static const uint32_t INVALID_FRAME = UINT32_MAX;
|
static const uint32_t INVALID_FRAME = UINT32_MAX;
|
||||||
|
@ -786,8 +786,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
|
if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
|
||||||
auto globalPos = QCursor::pos();
|
auto reticlePos = _compositor.getReticlePosition();
|
||||||
auto localPos = _glWidget->mapFromGlobal(globalPos);
|
QPoint globalPos(reticlePos.x, reticlePos.y);
|
||||||
|
|
||||||
|
// FIXME - it would be nice if this was self contained in the _compositor or Reticle class
|
||||||
|
auto localPos = isHMDMode() ? globalPos : _glWidget->mapFromGlobal(globalPos);
|
||||||
if (state) {
|
if (state) {
|
||||||
QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
||||||
sendEvent(_glWidget, &mousePress);
|
sendEvent(_glWidget, &mousePress);
|
||||||
|
@ -806,39 +809,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||||
cycleCamera();
|
cycleCamera();
|
||||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QCursor::pos()));
|
auto reticlePosition = _compositor.getReticlePosition();
|
||||||
|
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||||
auto oldPos = QCursor::pos();
|
auto oldPos = _compositor.getReticlePosition();
|
||||||
auto newPos = oldPos;
|
_compositor.setReticlePosition({ oldPos.x + state, oldPos.y });
|
||||||
newPos.setX(oldPos.x() + state);
|
|
||||||
QCursor::setPos(newPos);
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
|
||||||
// remove it after we're done
|
|
||||||
const float REASONABLE_CHANGE = 50.0f;
|
|
||||||
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
|
||||||
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
|
||||||
auto distance = glm::distance(oldPosG, newPosG);
|
|
||||||
if (distance > REASONABLE_CHANGE) {
|
|
||||||
qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
||||||
auto oldPos = QCursor::pos();
|
auto oldPos = _compositor.getReticlePosition();
|
||||||
auto newPos = oldPos;
|
_compositor.setReticlePosition({ oldPos.x, oldPos.y + state });
|
||||||
newPos.setY(oldPos.y() + state);
|
|
||||||
QCursor::setPos(newPos);
|
|
||||||
|
|
||||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
|
||||||
// remove it after we're done
|
|
||||||
const float REASONABLE_CHANGE = 50.0f;
|
|
||||||
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
|
||||||
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
|
||||||
auto distance = glm::distance(oldPosG, newPosG);
|
|
||||||
if (distance > REASONABLE_CHANGE) {
|
|
||||||
qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -980,7 +958,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
|
|
||||||
// If the user clicks somewhere where there is NO entity at all, we will release focus
|
// If the user clicks somewhere where there is NO entity at all, we will release focus
|
||||||
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,
|
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,
|
||||||
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) {
|
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) {
|
||||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||||
if (_keyboardFocusHighlight) {
|
if (_keyboardFocusHighlight) {
|
||||||
_keyboardFocusHighlight->setVisible(false);
|
_keyboardFocusHighlight->setVisible(false);
|
||||||
|
@ -1279,13 +1257,15 @@ void Application::initializeUi() {
|
||||||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||||
rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
||||||
|
rootContext->setContextProperty("Reticle", _compositor.getReticleInterface());
|
||||||
|
|
||||||
_glWidget->installEventFilter(offscreenUi.data());
|
_glWidget->installEventFilter(offscreenUi.data());
|
||||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||||
QPointF result = pt;
|
QPointF result = pt;
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
if (displayPlugin->isHmd()) {
|
if (displayPlugin->isHmd()) {
|
||||||
auto resultVec = _compositor.screenToOverlay(toGlm(pt));
|
_compositor.handleRealMouseMoveEvent(false);
|
||||||
|
auto resultVec = _compositor.getReticlePosition();
|
||||||
result = QPointF(resultVec.x, resultVec.y);
|
result = QPointF(resultVec.x, resultVec.y);
|
||||||
}
|
}
|
||||||
return result.toPoint();
|
return result.toPoint();
|
||||||
|
@ -1635,13 +1615,7 @@ void Application::paintGL() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
|
|
||||||
// include time waiting for sync, and which can report a number above target if we've got the headroom.
|
|
||||||
// In my tests, the following is mostly less than 0.5ms, and never more than 3ms. I don't think its worth measuring during runtime.
|
|
||||||
const float paintWaitAndQTTimerAllowance = 0.001f; // seconds
|
|
||||||
// Store both values now for use by next cycle.
|
|
||||||
_lastInstantaneousFps = instantaneousFps;
|
_lastInstantaneousFps = instantaneousFps;
|
||||||
_lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance);
|
|
||||||
_pendingPaint = false;
|
_pendingPaint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1733,6 +1707,7 @@ bool Application::event(QEvent* event) {
|
||||||
|
|
||||||
if ((int)event->type() == (int)Paint) {
|
if ((int)event->type() == (int)Paint) {
|
||||||
paintGL();
|
paintGL();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_keyboardFocusedItem.isInvalidID()) {
|
if (!_keyboardFocusedItem.isInvalidID()) {
|
||||||
|
@ -1824,6 +1799,11 @@ bool Application::event(QEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||||
|
|
||||||
|
if (event->type() == QEvent::Leave) {
|
||||||
|
_compositor.handleLeaveEvent();
|
||||||
|
}
|
||||||
|
|
||||||
if (event->type() == QEvent::ShortcutOverride) {
|
if (event->type() == QEvent::ShortcutOverride) {
|
||||||
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
|
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
|
||||||
event->accept();
|
event->accept();
|
||||||
|
@ -2079,9 +2059,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Space: {
|
case Qt::Key_Space: {
|
||||||
if (!event->isAutoRepeat()) {
|
if (!event->isAutoRepeat()) {
|
||||||
|
// FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious
|
||||||
// this starts an HFActionEvent
|
// this starts an HFActionEvent
|
||||||
HFActionEvent startActionEvent(HFActionEvent::startType(),
|
HFActionEvent startActionEvent(HFActionEvent::startType(),
|
||||||
computePickRay(getTrueMouse().x, getTrueMouse().y));
|
computePickRay(getMouse().x, getMouse().y));
|
||||||
sendEvent(this, &startActionEvent);
|
sendEvent(this, &startActionEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2110,7 +2091,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QCursor::pos()));
|
auto reticlePosition = _compositor.getReticlePosition();
|
||||||
|
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_keysPressed.remove(event->key());
|
_keysPressed.remove(event->key());
|
||||||
|
@ -2129,9 +2111,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_Space: {
|
case Qt::Key_Space: {
|
||||||
if (!event->isAutoRepeat()) {
|
if (!event->isAutoRepeat()) {
|
||||||
|
// FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious
|
||||||
// this ends the HFActionEvent
|
// this ends the HFActionEvent
|
||||||
HFActionEvent endActionEvent(HFActionEvent::endType(),
|
HFActionEvent endActionEvent(HFActionEvent::endType(),
|
||||||
computePickRay(getTrueMouse().x, getTrueMouse().y));
|
computePickRay(getMouse().x, getMouse().y));
|
||||||
sendEvent(this, &endActionEvent);
|
sendEvent(this, &endActionEvent);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2175,13 +2158,7 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
||||||
_keysPressed.clear();
|
_keysPressed.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::maybeToggleMenuVisible(QMouseEvent* event) {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
|
||||||
|
|
||||||
if (_aboutToQuit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
// If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow
|
// If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow
|
||||||
// then show the menubar.
|
// then show the menubar.
|
||||||
|
@ -2193,7 +2170,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
if (event->pos().y() <= MENU_TOGGLE_AREA) {
|
if (event->pos().y() <= MENU_TOGGLE_AREA) {
|
||||||
menuBar->setVisible(true);
|
menuBar->setVisible(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (event->pos().y() > MENU_TOGGLE_AREA) {
|
if (event->pos().y() > MENU_TOGGLE_AREA) {
|
||||||
menuBar->setVisible(false);
|
menuBar->setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -2201,9 +2178,36 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement
|
||||||
|
void Application::fakeMouseEvent(QMouseEvent* event) {
|
||||||
|
_fakedMouseEvent = true;
|
||||||
|
sendEvent(_glWidget, event);
|
||||||
|
_fakedMouseEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeToggleMenuVisible(event);
|
||||||
|
|
||||||
|
// if this is a real mouse event, and we're in HMD mode, then we should use it to move the
|
||||||
|
// compositor reticle
|
||||||
|
if (!_fakedMouseEvent) {
|
||||||
|
// handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
|
||||||
|
if (_compositor.handleRealMouseMoveEvent()) {
|
||||||
|
return; // bail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||||
|
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||||
auto button = event->button();
|
auto button = event->button();
|
||||||
auto buttons = event->buttons();
|
auto buttons = event->buttons();
|
||||||
// Determine if the ReticleClick Action is 1 and if so, fake include the LeftMouseButton
|
// Determine if the ReticleClick Action is 1 and if so, fake include the LeftMouseButton
|
||||||
|
@ -2219,21 +2223,21 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
event->screenPos(), button,
|
event->screenPos(), button,
|
||||||
buttons, event->modifiers());
|
buttons, event->modifiers());
|
||||||
|
|
||||||
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
|
getEntities()->mouseMoveEvent(&mappedEvent);
|
||||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
|
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
|
||||||
|
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
if (_controllerScriptingInterface->isMouseCaptured()) {
|
if (_controllerScriptingInterface->isMouseCaptured()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||||
_keyboardMouseDevice->mouseMoveEvent(event, deviceID);
|
_keyboardMouseDevice->mouseMoveEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::mousePressEvent(QMouseEvent* event) {
|
||||||
// Inhibit the menu if the user is using alt-mouse dragging
|
// Inhibit the menu if the user is using alt-mouse dragging
|
||||||
_altPressed = false;
|
_altPressed = false;
|
||||||
|
|
||||||
|
@ -2243,14 +2247,16 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
// keyboard shortcuts not to be swallowed by them. In particular, WebEngineViews
|
// keyboard shortcuts not to be swallowed by them. In particular, WebEngineViews
|
||||||
// will consume all keyboard events.
|
// will consume all keyboard events.
|
||||||
offscreenUi->unfocusWindows();
|
offscreenUi->unfocusWindows();
|
||||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
|
||||||
|
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||||
|
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||||
QMouseEvent mappedEvent(event->type(),
|
QMouseEvent mappedEvent(event->type(),
|
||||||
transformedPos,
|
transformedPos,
|
||||||
event->screenPos(), event->button(),
|
event->screenPos(), event->button(),
|
||||||
event->buttons(), event->modifiers());
|
event->buttons(), event->modifiers());
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
getEntities()->mousePressEvent(&mappedEvent, deviceID);
|
getEntities()->mousePressEvent(&mappedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
|
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
|
||||||
|
@ -2262,7 +2268,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
|
||||||
|
|
||||||
if (hasFocus()) {
|
if (hasFocus()) {
|
||||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||||
_keyboardMouseDevice->mousePressEvent(event);
|
_keyboardMouseDevice->mousePressEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2276,7 +2282,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::mouseDoublePressEvent(QMouseEvent* event) {
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
if (_controllerScriptingInterface->isMouseCaptured()) {
|
if (_controllerScriptingInterface->isMouseCaptured()) {
|
||||||
return;
|
return;
|
||||||
|
@ -2285,17 +2291,18 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
_controllerScriptingInterface->emitMouseDoublePressEvent(event);
|
_controllerScriptingInterface->emitMouseDoublePressEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||||
|
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||||
QMouseEvent mappedEvent(event->type(),
|
QMouseEvent mappedEvent(event->type(),
|
||||||
transformedPos,
|
transformedPos,
|
||||||
event->screenPos(), event->button(),
|
event->screenPos(), event->button(),
|
||||||
event->buttons(), event->modifiers());
|
event->buttons(), event->modifiers());
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
getEntities()->mouseReleaseEvent(&mappedEvent, deviceID);
|
getEntities()->mouseReleaseEvent(&mappedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
|
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
|
||||||
|
@ -2306,7 +2313,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasFocus()) {
|
if (hasFocus()) {
|
||||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||||
_keyboardMouseDevice->mouseReleaseEvent(event);
|
_keyboardMouseDevice->mouseReleaseEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2566,11 +2573,16 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||||
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Application::getMouse() const {
|
ivec2 Application::getMouse() {
|
||||||
|
auto reticlePosition = _compositor.getReticlePosition();
|
||||||
|
|
||||||
|
// in the HMD, the reticlePosition is the mouse position
|
||||||
if (isHMDMode()) {
|
if (isHMDMode()) {
|
||||||
return _compositor.screenToOverlay(getTrueMouse());
|
return reticlePosition;
|
||||||
}
|
}
|
||||||
return getTrueMouse();
|
|
||||||
|
// in desktop mode, we need to map from global to widget space
|
||||||
|
return toGlm(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
FaceTracker* Application::getActiveFaceTracker() {
|
FaceTracker* Application::getActiveFaceTracker() {
|
||||||
|
@ -3082,11 +3094,7 @@ void Application::update(float deltaTime) {
|
||||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||||
|
|
||||||
if (DependencyManager::get<LODManager>()->getUseAcuity()) {
|
updateLOD();
|
||||||
updateLOD();
|
|
||||||
} else {
|
|
||||||
DependencyManager::get<LODManager>()->updatePIDRenderDistance(getTargetFrameRate(), getLastInstanteousFps(), deltaTime, isThrottleRendering());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("devices");
|
PerformanceTimer perfTimer("devices");
|
||||||
|
@ -4240,6 +4248,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||||
|
scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::canAcceptURL(const QString& urlString) const {
|
bool Application::canAcceptURL(const QString& urlString) const {
|
||||||
|
@ -4711,6 +4720,14 @@ glm::uvec2 Application::getCanvasSize() const {
|
||||||
return glm::uvec2(_glWidget->width(), _glWidget->height());
|
return glm::uvec2(_glWidget->width(), _glWidget->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect Application::getRenderingGeometry() const {
|
||||||
|
auto geometry = _glWidget->geometry();
|
||||||
|
auto topLeft = geometry.topLeft();
|
||||||
|
auto topLeftScreen = _glWidget->mapToGlobal(topLeft);
|
||||||
|
geometry.moveTopLeft(topLeftScreen);
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
glm::uvec2 Application::getUiSize() const {
|
glm::uvec2 Application::getUiSize() const {
|
||||||
return getActiveDisplayPlugin()->getRecommendedUiSize();
|
return getActiveDisplayPlugin()->getRecommendedUiSize();
|
||||||
}
|
}
|
||||||
|
@ -4719,18 +4736,10 @@ QSize Application::getDeviceSize() const {
|
||||||
return fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
return fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
PickRay Application::computePickRay() const {
|
|
||||||
return computePickRay(getTrueMouse().x, getTrueMouse().y);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::isThrottleRendering() const {
|
bool Application::isThrottleRendering() const {
|
||||||
return getActiveDisplayPlugin()->isThrottled();
|
return getActiveDisplayPlugin()->isThrottled();
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Application::getTrueMouse() const {
|
|
||||||
return toGlm(_glWidget->mapFromGlobal(QCursor::pos()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::hasFocus() const {
|
bool Application::hasFocus() const {
|
||||||
return getActiveDisplayPlugin()->hasFocus();
|
return getActiveDisplayPlugin()->hasFocus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,10 +114,11 @@ public:
|
||||||
bool eventFilter(QObject* object, QEvent* event) override;
|
bool eventFilter(QObject* object, QEvent* event) override;
|
||||||
|
|
||||||
glm::uvec2 getCanvasSize() const;
|
glm::uvec2 getCanvasSize() const;
|
||||||
|
QRect getRenderingGeometry() const;
|
||||||
|
|
||||||
glm::uvec2 getUiSize() const;
|
glm::uvec2 getUiSize() const;
|
||||||
QSize getDeviceSize() const;
|
QSize getDeviceSize() const;
|
||||||
bool hasFocus() const;
|
bool hasFocus() const;
|
||||||
PickRay computePickRay() const;
|
|
||||||
|
|
||||||
bool isThrottleRendering() const;
|
bool isThrottleRendering() const;
|
||||||
|
|
||||||
|
@ -139,8 +140,7 @@ public:
|
||||||
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
||||||
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
||||||
|
|
||||||
ivec2 getMouse() const;
|
ivec2 getMouse();
|
||||||
ivec2 getTrueMouse() const;
|
|
||||||
|
|
||||||
FaceTracker* getActiveFaceTracker();
|
FaceTracker* getActiveFaceTracker();
|
||||||
FaceTracker* getSelectedFaceTracker();
|
FaceTracker* getSelectedFaceTracker();
|
||||||
|
@ -157,7 +157,6 @@ public:
|
||||||
float getFps() const { return _fps; }
|
float getFps() const { return _fps; }
|
||||||
float getTargetFrameRate(); // frames/second
|
float getTargetFrameRate(); // frames/second
|
||||||
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
||||||
float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; }
|
|
||||||
|
|
||||||
float getFieldOfView() { return _fieldOfView.get(); }
|
float getFieldOfView() { return _fieldOfView.get(); }
|
||||||
void setFieldOfView(float fov);
|
void setFieldOfView(float fov);
|
||||||
|
@ -219,6 +218,8 @@ public:
|
||||||
|
|
||||||
float getAverageSimsPerSecond();
|
float getAverageSimsPerSecond();
|
||||||
|
|
||||||
|
void fakeMouseEvent(QMouseEvent* event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
|
||||||
|
@ -368,10 +369,10 @@ private:
|
||||||
void focusOutEvent(QFocusEvent* event);
|
void focusOutEvent(QFocusEvent* event);
|
||||||
void focusInEvent(QFocusEvent* event);
|
void focusInEvent(QFocusEvent* event);
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mouseDoublePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
|
||||||
void touchBeginEvent(QTouchEvent* event);
|
void touchBeginEvent(QTouchEvent* event);
|
||||||
void touchEndEvent(QTouchEvent* event);
|
void touchEndEvent(QTouchEvent* event);
|
||||||
|
@ -381,6 +382,7 @@ private:
|
||||||
void dropEvent(QDropEvent* event);
|
void dropEvent(QDropEvent* event);
|
||||||
void dragEnterEvent(QDragEnterEvent* event);
|
void dragEnterEvent(QDragEnterEvent* event);
|
||||||
|
|
||||||
|
void maybeToggleMenuVisible(QMouseEvent* event);
|
||||||
|
|
||||||
bool _dependencyManagerIsSetup;
|
bool _dependencyManagerIsSetup;
|
||||||
|
|
||||||
|
@ -402,7 +404,6 @@ private:
|
||||||
QElapsedTimer _timerStart;
|
QElapsedTimer _timerStart;
|
||||||
QElapsedTimer _lastTimeUpdated;
|
QElapsedTimer _lastTimeUpdated;
|
||||||
float _lastInstantaneousFps { 0.0f };
|
float _lastInstantaneousFps { 0.0f };
|
||||||
float _lastUnsynchronizedFps { 0.0f };
|
|
||||||
|
|
||||||
ShapeManager _shapeManager;
|
ShapeManager _shapeManager;
|
||||||
PhysicalEntitySimulation _entitySimulation;
|
PhysicalEntitySimulation _entitySimulation;
|
||||||
|
@ -510,6 +511,8 @@ private:
|
||||||
bool _settingsLoaded { false };
|
bool _settingsLoaded { false };
|
||||||
bool _pendingPaint { false };
|
bool _pendingPaint { false };
|
||||||
QTimer* _idleTimer { nullptr };
|
QTimer* _idleTimer { nullptr };
|
||||||
|
|
||||||
|
bool _fakedMouseEvent { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -20,29 +20,8 @@
|
||||||
|
|
||||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
||||||
// There are two different systems in use, based on lodPreference:
|
|
||||||
// pid: renderDistance is adjusted by a PID such that frame rate targets are met.
|
|
||||||
// acuity: a pseudo-acuity target is held, or adjusted to match minimum frame rates (and a PID controlls avatar rendering distance)
|
|
||||||
// If unspecified, acuity is used only if user has specified non-default minumum frame rates.
|
|
||||||
Setting::Handle<int> lodPreference("lodPreference", (int)LODManager::LODPreference::acuity);
|
|
||||||
const float SMALLEST_REASONABLE_HORIZON = 50.0f; // meters
|
|
||||||
Setting::Handle<float> renderDistanceInverseHighLimit("renderDistanceInverseHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
|
|
||||||
void LODManager::setRenderDistanceInverseHighLimit(float newValue) {
|
|
||||||
renderDistanceInverseHighLimit.set(newValue); // persist it, and tell all the controllers that use it
|
|
||||||
_renderDistanceController.setControlledValueHighLimit(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
LODManager::LODManager() {
|
LODManager::LODManager() {
|
||||||
|
|
||||||
setRenderDistanceInverseHighLimit(renderDistanceInverseHighLimit.get());
|
|
||||||
setRenderDistanceInverseLowLimit(1.0f / (float)TREE_SCALE);
|
|
||||||
// Advice for tuning parameters:
|
|
||||||
// See PIDController.h. There's a section on tuning in the reference.
|
|
||||||
// Turn on logging with the following (or from js with LODManager.setRenderDistanceControllerHistory("render pid", 240))
|
|
||||||
//setRenderDistanceControllerHistory("render pid", 60 * 4);
|
|
||||||
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
|
|
||||||
setRenderDistanceKP(0.000012f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
|
|
||||||
setRenderDistanceKI(0.00002f); // Big enough to bring us to target with the above KP.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float LODManager::getLODDecreaseFPS() {
|
float LODManager::getLODDecreaseFPS() {
|
||||||
|
@ -234,53 +213,7 @@ QString LODManager::getLODFeedbackText() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float renderDistance = (float)TREE_SCALE;
|
|
||||||
static int renderedCount = 0;
|
|
||||||
static int lastRenderedCount = 0;
|
|
||||||
bool LODManager::getUseAcuity() { return lodPreference.get() == (int)LODManager::LODPreference::acuity; }
|
|
||||||
void LODManager::setUseAcuity(bool newValue) { lodPreference.set(newValue ? (int)LODManager::LODPreference::acuity : (int)LODManager::LODPreference::pid); }
|
|
||||||
float LODManager::getRenderDistance() {
|
|
||||||
return renderDistance;
|
|
||||||
}
|
|
||||||
int LODManager::getRenderedCount() {
|
|
||||||
return lastRenderedCount;
|
|
||||||
}
|
|
||||||
QString LODManager::getLODStatsRenderText() {
|
|
||||||
const QString label = "Rendered objects: ";
|
|
||||||
return label + QString::number(getRenderedCount()) + " w/in " + QString::number((int)getRenderDistance()) + "m";
|
|
||||||
}
|
|
||||||
// compare autoAdjustLOD()
|
|
||||||
void LODManager::updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled) {
|
|
||||||
float distance;
|
|
||||||
if (!isThrottled) {
|
|
||||||
_renderDistanceController.setMeasuredValueSetpoint(targetFps); // No problem updating in flight.
|
|
||||||
// The PID controller raises the controlled value when the measured value goes up.
|
|
||||||
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
|
|
||||||
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
|
|
||||||
// goes up.
|
|
||||||
distance = 1.0f / _renderDistanceController.update(measuredFps, deltaTime);
|
|
||||||
} else {
|
|
||||||
// Here we choose to just use the maximum render cutoff distance if throttled.
|
|
||||||
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
|
|
||||||
}
|
|
||||||
_renderDistanceAverage.updateAverage(distance);
|
|
||||||
renderDistance = _renderDistanceAverage.getAverage(); // average only once per cycle
|
|
||||||
lastRenderedCount = renderedCount;
|
|
||||||
renderedCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
|
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
|
||||||
// NOTE: this branch of code is the alternate form of LOD that uses PID controllers.
|
|
||||||
if (!getUseAcuity()) {
|
|
||||||
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
|
|
||||||
float largestDimension = bounds.getLargestDimension();
|
|
||||||
const float scenerySize = 300; // meters
|
|
||||||
bool isRendered = (largestDimension > scenerySize) || // render scenery regardless of distance
|
|
||||||
(distanceToCamera < renderDistance + largestDimension);
|
|
||||||
renderedCount += isRendered ? 1 : 0;
|
|
||||||
return isRendered;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME - eventually we want to use the render accuracy as an indicator for the level of detail
|
// FIXME - eventually we want to use the render accuracy as an indicator for the level of detail
|
||||||
// to use in rendering.
|
// to use in rendering.
|
||||||
float renderAccuracy = args->_viewFrustum->calculateRenderAccuracy(bounds, args->_sizeScale, args->_boundaryLevelAdjust);
|
float renderAccuracy = args->_viewFrustum->calculateRenderAccuracy(bounds, args->_sizeScale, args->_boundaryLevelAdjust);
|
||||||
|
@ -299,12 +232,6 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
||||||
void LODManager::loadSettings() {
|
void LODManager::loadSettings() {
|
||||||
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
|
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
|
||||||
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
|
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
|
||||||
|
|
||||||
if (lodPreference.get() == (int)LODManager::LODPreference::unspecified) {
|
|
||||||
setUseAcuity((getDesktopLODDecreaseFPS() != DEFAULT_DESKTOP_LOD_DOWN_FPS) || (getHMDLODDecreaseFPS() != DEFAULT_HMD_LOD_DOWN_FPS));
|
|
||||||
}
|
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(getUseAcuity());
|
|
||||||
Menu::getInstance()->getSubMenuFromName(MenuOption::RenderResolution, Menu::getInstance()->getSubMenuFromName("Render", Menu::getInstance()->getMenu("Developer")))->setEnabled(getUseAcuity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LODManager::saveSettings() {
|
void LODManager::saveSettings() {
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
#include <PIDController.h>
|
#include <PIDController.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
|
|
||||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0;
|
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0;
|
||||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 30.0;
|
const float DEFAULT_HMD_LOD_DOWN_FPS = 45.0;
|
||||||
const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps
|
const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps
|
||||||
const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
|
const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
|
||||||
const float INCREASE_LOD_GAP = 15.0f;
|
const float INCREASE_LOD_GAP = 15.0f;
|
||||||
|
@ -76,27 +76,6 @@ public:
|
||||||
Q_INVOKABLE float getLODDecreaseFPS();
|
Q_INVOKABLE float getLODDecreaseFPS();
|
||||||
Q_INVOKABLE float getLODIncreaseFPS();
|
Q_INVOKABLE float getLODIncreaseFPS();
|
||||||
|
|
||||||
enum class LODPreference {
|
|
||||||
pid = 0,
|
|
||||||
acuity,
|
|
||||||
unspecified
|
|
||||||
};
|
|
||||||
static bool getUseAcuity();
|
|
||||||
static void setUseAcuity(bool newValue);
|
|
||||||
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
|
|
||||||
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
|
|
||||||
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
|
|
||||||
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
|
|
||||||
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
|
|
||||||
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
|
|
||||||
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
|
||||||
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
|
|
||||||
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
|
|
||||||
void updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled);
|
|
||||||
float getRenderDistance();
|
|
||||||
int getRenderedCount();
|
|
||||||
QString getLODStatsRenderText();
|
|
||||||
|
|
||||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||||
void autoAdjustLOD(float currentFPS);
|
void autoAdjustLOD(float currentFPS);
|
||||||
|
|
||||||
|
@ -126,9 +105,6 @@ private:
|
||||||
SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES;
|
SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES;
|
||||||
SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES;
|
SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES;
|
||||||
SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES;
|
SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES;
|
||||||
|
|
||||||
PIDController _renderDistanceController{};
|
|
||||||
SimpleMovingAverage _renderDistanceAverage{ 10 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_LODManager_h
|
#endif // hifi_LODManager_h
|
||||||
|
|
|
@ -968,6 +968,7 @@ int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPositi
|
||||||
return requestedPosition;
|
return requestedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Menu::_isSomeSubmenuShown = false;
|
||||||
|
|
||||||
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||||
QStringList menuTree = menuName.split(">");
|
QStringList menuTree = menuName.split(">");
|
||||||
|
@ -994,6 +995,12 @@ MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenuBar::repaint();
|
QMenuBar::repaint();
|
||||||
|
|
||||||
|
// hook our show/hide for popup menus, so we can keep track of whether or not one
|
||||||
|
// of our submenus is currently showing.
|
||||||
|
connect(menu->_realMenu, &QMenu::aboutToShow, []() { _isSomeSubmenuShown = true; });
|
||||||
|
connect(menu->_realMenu, &QMenu::aboutToHide, []() { _isSomeSubmenuShown = false; });
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
QAction* newAction() {
|
QAction* newAction() {
|
||||||
return new QAction(_realMenu);
|
return new QAction(_realMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuWrapper(QMenu* menu);
|
MenuWrapper(QMenu* menu);
|
||||||
|
|
||||||
|
@ -117,6 +118,8 @@ public slots:
|
||||||
void toggleDeveloperMenus();
|
void toggleDeveloperMenus();
|
||||||
void toggleAdvancedMenus();
|
void toggleAdvancedMenus();
|
||||||
|
|
||||||
|
static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef void(*settingsAction)(Settings&, QAction&);
|
typedef void(*settingsAction)(Settings&, QAction&);
|
||||||
static void loadAction(Settings& settings, QAction& action);
|
static void loadAction(Settings& settings, QAction& action);
|
||||||
|
@ -142,6 +145,8 @@ private:
|
||||||
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
||||||
QHash<QString, bool> _groupingVisible;
|
QHash<QString, bool> _groupingVisible;
|
||||||
QHash<QString, QSet<QAction*>> _groupingActions;
|
QHash<QString, QSet<QAction*>> _groupingActions;
|
||||||
|
|
||||||
|
static bool _isSomeSubmenuShown;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MenuOption {
|
namespace MenuOption {
|
||||||
|
|
|
@ -162,7 +162,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
|
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
|
||||||
|
|
||||||
const float SHRINK_RATE = 0.9f;
|
const float SHRINK_RATE = 0.15f;
|
||||||
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
|
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
|
||||||
|
|
||||||
render::ScenePointer scene = qApp->getMain3DScene();
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
|
|
|
@ -164,10 +164,10 @@ InputController::Key InputController::getKey() const {
|
||||||
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
|
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
|
||||||
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
|
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
|
||||||
|
|
||||||
void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
|
void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); }
|
||||||
void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
|
void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); }
|
||||||
void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
|
void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event) { emit mouseDoublePressEvent(MouseEvent(*event)); }
|
||||||
void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
|
void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); }
|
||||||
|
|
||||||
void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
|
void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
|
||||||
void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
|
void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
|
||||||
|
|
|
@ -70,10 +70,10 @@ public:
|
||||||
|
|
||||||
void handleMetaEvent(HFMetaEvent* event);
|
void handleMetaEvent(HFMetaEvent* event);
|
||||||
|
|
||||||
void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void emitMouseMoveEvent(QMouseEvent* event);
|
||||||
void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void emitMousePressEvent(QMouseEvent* event);
|
||||||
void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void emitMouseDoublePressEvent(QMouseEvent* event);
|
||||||
void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void emitMouseReleaseEvent(QMouseEvent* event);
|
||||||
|
|
||||||
void emitTouchBeginEvent(const TouchEvent& event);
|
void emitTouchBeginEvent(const TouchEvent& event);
|
||||||
void emitTouchEndEvent(const TouchEvent& event);
|
void emitTouchEndEvent(const TouchEvent& event);
|
||||||
|
@ -111,10 +111,10 @@ signals:
|
||||||
void backStartEvent();
|
void backStartEvent();
|
||||||
void backEndEvent();
|
void backEndEvent();
|
||||||
|
|
||||||
void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
void mouseMoveEvent(const MouseEvent& event);
|
||||||
void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
void mousePressEvent(const MouseEvent& event);
|
||||||
void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
void mouseDoublePressEvent(const MouseEvent& event);
|
||||||
void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
void mouseReleaseEvent(const MouseEvent& event);
|
||||||
|
|
||||||
void touchBeginEvent(const TouchEvent& event);
|
void touchBeginEvent(const TouchEvent& event);
|
||||||
void touchEndEvent(const TouchEvent& event);
|
void touchEndEvent(const TouchEvent& event);
|
||||||
|
|
|
@ -30,6 +30,10 @@ glm::vec2 HMDScriptingInterface::overlayFromWorldPoint(const glm::vec3& position
|
||||||
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
|
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 HMDScriptingInterface::worldPointFromOverlay(const glm::vec2& overlay) const {
|
||||||
|
return qApp->getApplicationCompositor().sphereSurfaceFromOverlay(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 HMDScriptingInterface::sphericalToOverlay(const glm::vec2 & position) const {
|
glm::vec2 HMDScriptingInterface::sphericalToOverlay(const glm::vec2 & position) const {
|
||||||
return qApp->getApplicationCompositor().sphericalToOverlay(position);
|
return qApp->getApplicationCompositor().sphericalToOverlay(position);
|
||||||
}
|
}
|
||||||
|
@ -38,16 +42,6 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position)
|
||||||
return qApp->getApplicationCompositor().overlayToSpherical(position);
|
return qApp->getApplicationCompositor().overlayToSpherical(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 HMDScriptingInterface::screenToOverlay(const glm::vec2 & position) const {
|
|
||||||
return qApp->getApplicationCompositor().screenToOverlay(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec2 HMDScriptingInterface::overlayToScreen(const glm::vec2 & position) const {
|
|
||||||
return qApp->getApplicationCompositor().overlayToScreen(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
||||||
glm::vec3 hudIntersection;
|
glm::vec3 hudIntersection;
|
||||||
auto instance = DependencyManager::get<HMDScriptingInterface>();
|
auto instance = DependencyManager::get<HMDScriptingInterface>();
|
||||||
|
|
|
@ -29,11 +29,10 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
|
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
|
||||||
Q_INVOKABLE glm::vec2 overlayFromWorldPoint(const glm::vec3& position) const;
|
Q_INVOKABLE glm::vec2 overlayFromWorldPoint(const glm::vec3& position) const;
|
||||||
|
Q_INVOKABLE glm::vec3 worldPointFromOverlay(const glm::vec2& overlay) const;
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||||
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||||
Q_INVOKABLE glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
|
|
||||||
Q_INVOKABLE glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HMDScriptingInterface();
|
HMDScriptingInterface();
|
||||||
|
|
|
@ -64,18 +64,6 @@ void WindowScriptingInterface::raiseMainWindow() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowScriptingInterface::setCursorPosition(int x, int y) {
|
|
||||||
QCursor::setPos(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::getCursorPositionX() {
|
|
||||||
return QCursor::pos().x();
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::getCursorPositionY() {
|
|
||||||
return QCursor::pos().y();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Display an alert box
|
/// Display an alert box
|
||||||
/// \param const QString& message message to display
|
/// \param const QString& message message to display
|
||||||
/// \return QScriptValue::UndefinedValue
|
/// \return QScriptValue::UndefinedValue
|
||||||
|
|
|
@ -32,9 +32,6 @@ public:
|
||||||
int getY();
|
int getY();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QScriptValue getCursorPositionX();
|
|
||||||
QScriptValue getCursorPositionY();
|
|
||||||
void setCursorPosition(int x, int y);
|
|
||||||
QScriptValue hasFocus();
|
QScriptValue hasFocus();
|
||||||
void setFocus();
|
void setFocus();
|
||||||
void raiseMainWindow();
|
void raiseMainWindow();
|
||||||
|
|
|
@ -37,9 +37,6 @@ static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||||
static const float reticleSize = TWO_PI / 100.0f;
|
static const float reticleSize = TWO_PI / 100.0f;
|
||||||
|
|
||||||
static const float CURSOR_PIXEL_SIZE = 32.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 _hemiVertices;
|
||||||
static gpu::BufferPointer _hemiIndices;
|
static gpu::BufferPointer _hemiIndices;
|
||||||
|
@ -112,7 +109,9 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r,
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationCompositor::ApplicationCompositor() :
|
ApplicationCompositor::ApplicationCompositor() :
|
||||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha"))
|
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")),
|
||||||
|
_reticleInterface(new ReticleInterface(this))
|
||||||
|
|
||||||
{
|
{
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
|
@ -214,7 +213,7 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||||
|
|
||||||
//draw the mouse pointer
|
//draw the mouse pointer
|
||||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||||
vec2 canvasSize = qApp->getCanvasSize();
|
vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas...
|
||||||
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||||
// Invert the Y axis
|
// Invert the Y axis
|
||||||
mousePosition.y *= -1.0f;
|
mousePosition.y *= -1.0f;
|
||||||
|
@ -244,7 +243,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
|
|
||||||
updateTooltips();
|
updateTooltips();
|
||||||
|
|
||||||
vec2 canvasSize = qApp->getCanvasSize();
|
glm::uvec2 screenSize = qApp->getUiSize(); // HMD use virtual screen size
|
||||||
|
vec2 canvasSize = screenSize;
|
||||||
_textureAspectRatio = aspect(canvasSize);
|
_textureAspectRatio = aspect(canvasSize);
|
||||||
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
@ -257,9 +257,9 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
mat4 camMat;
|
mat4 camMat;
|
||||||
_cameraBaseTransform.getMatrix(camMat);
|
_cameraBaseTransform.getMatrix(camMat);
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
auto headPose = qApp->getHMDSensorPose();
|
||||||
auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye);
|
auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye);
|
||||||
camMat = (headPose * eyeToHead) * camMat;
|
camMat = (headPose * eyeToHead) * camMat; // FIXME - why are not all transforms are doing this aditioanl eyeToHead
|
||||||
batch.setViewportTransform(renderArgs->_viewport);
|
batch.setViewportTransform(renderArgs->_viewport);
|
||||||
batch.setViewTransform(camMat);
|
batch.setViewTransform(camMat);
|
||||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||||
|
@ -282,15 +282,13 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
bindCursorTexture(batch);
|
bindCursorTexture(batch);
|
||||||
|
|
||||||
//Mouse Pointer
|
//Mouse Pointer
|
||||||
auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
|
if (getReticleVisible()) {
|
||||||
bool reticleVisible = controllerScriptingInterface->getReticleVisible();
|
|
||||||
if (reticleVisible) {
|
|
||||||
glm::mat4 overlayXfm;
|
glm::mat4 overlayXfm;
|
||||||
_modelTransform.getMatrix(overlayXfm);
|
_modelTransform.getMatrix(overlayXfm);
|
||||||
|
|
||||||
glm::vec2 projection = screenToSpherical(qApp->getTrueMouse());
|
auto reticlePosition = getReticlePosition();
|
||||||
|
glm::vec2 projection = overlayToSpherical(reticlePosition);
|
||||||
float cursorDepth = controllerScriptingInterface->getReticleDepth();
|
float cursorDepth = getReticleDepth();
|
||||||
mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||||
|
@ -300,42 +298,147 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) {
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
QMutexLocker locker(&_reticlePositionInHMDLock);
|
||||||
|
return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||||
|
}
|
||||||
|
return event->localPos();
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME - this probably is hella buggy and probably doesn't work correctly
|
bool ApplicationCompositor::shouldCaptureMouse() const {
|
||||||
// we should kill it asap.
|
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
|
||||||
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
return qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown();
|
||||||
const glm::vec2 projection = overlayToSpherical(cursorPos);
|
}
|
||||||
// The overlay space orientation of the mouse coordinates
|
|
||||||
const glm::quat cursorOrientation(glm::vec3(-projection.y, projection.x, 0.0f));
|
|
||||||
|
|
||||||
// The orientation and position of the HEAD, not the overlay
|
void ApplicationCompositor::handleLeaveEvent() {
|
||||||
glm::vec3 worldSpaceHeadPosition = qApp->getCamera()->getPosition();
|
|
||||||
glm::quat worldSpaceOrientation = qApp->getCamera()->getOrientation();
|
|
||||||
|
|
||||||
auto headPose = qApp->getHMDSensorPose();
|
if (shouldCaptureMouse()) {
|
||||||
auto headOrientation = glm::quat_cast(headPose);
|
QWidget* mainWidget = (QWidget*)qApp->getWindow();
|
||||||
auto headTranslation = extractTranslation(headPose);
|
QRect mainWidgetFrame = qApp->getRenderingGeometry();
|
||||||
|
QRect uncoveredRect = mainWidgetFrame;
|
||||||
|
foreach(QWidget* widget, QApplication::topLevelWidgets()) {
|
||||||
|
if (widget->isWindow() && widget->isVisible() && widget != mainWidget) {
|
||||||
|
QRect widgetFrame = widget->frameGeometry();
|
||||||
|
if (widgetFrame.intersects(uncoveredRect)) {
|
||||||
|
QRect intersection = uncoveredRect & widgetFrame;
|
||||||
|
if (intersection.top() > uncoveredRect.top()) {
|
||||||
|
uncoveredRect.setBottom(intersection.top() - 1);
|
||||||
|
} else if (intersection.bottom() < uncoveredRect.bottom()) {
|
||||||
|
uncoveredRect.setTop(intersection.bottom() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
auto overlayOrientation = worldSpaceOrientation * glm::inverse(headOrientation);
|
if (intersection.left() > uncoveredRect.left()) {
|
||||||
auto overlayPosition = worldSpaceHeadPosition - (overlayOrientation * headTranslation);
|
uncoveredRect.setRight(intersection.left() - 1);
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StandingHMDSensorMode)) {
|
} else if (intersection.right() < uncoveredRect.right()) {
|
||||||
overlayPosition = _modelTransform.getTranslation();
|
uncoveredRect.setLeft(intersection.right() + 1);
|
||||||
overlayOrientation = _modelTransform.getRotation();
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ignoreMouseMove = true;
|
||||||
|
auto sendToPos = uncoveredRect.center();
|
||||||
|
QCursor::setPos(sendToPos);
|
||||||
|
_lastKnownRealMouse = sendToPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) {
|
||||||
|
|
||||||
|
// If the mouse move came from a capture mouse related move, we completely ignore it.
|
||||||
|
if (_ignoreMouseMove) {
|
||||||
|
_ignoreMouseMove = false;
|
||||||
|
return true; // swallow the event
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intersection in world space
|
// If we're in HMD mode
|
||||||
glm::vec3 worldSpaceIntersection = ((overlayOrientation * (cursorOrientation * Vectors::FRONT)) * _oculusUIRadius) + overlayPosition;
|
if (shouldCaptureMouse()) {
|
||||||
|
QMutexLocker locker(&_reticlePositionInHMDLock);
|
||||||
|
auto newPosition = QCursor::pos();
|
||||||
|
auto changeInRealMouse = newPosition - _lastKnownRealMouse;
|
||||||
|
auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse);
|
||||||
|
setReticlePosition(newReticlePosition, sendFakeEvent);
|
||||||
|
_ignoreMouseMove = true;
|
||||||
|
QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was
|
||||||
|
return true; // swallow the event
|
||||||
|
} else {
|
||||||
|
_lastKnownRealMouse = QCursor::pos();
|
||||||
|
}
|
||||||
|
return false; // let the caller know to process the event
|
||||||
|
}
|
||||||
|
|
||||||
origin = worldSpaceHeadPosition;
|
glm::vec2 ApplicationCompositor::getReticlePosition() {
|
||||||
direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);
|
if (qApp->isHMDMode()) {
|
||||||
|
QMutexLocker locker(&_reticlePositionInHMDLock);
|
||||||
|
return _reticlePositionInHMD;
|
||||||
|
}
|
||||||
|
return toGlm(QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationCompositor::setReticlePosition(glm::vec2 position, bool sendFakeEvent) {
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
QMutexLocker locker(&_reticlePositionInHMDLock);
|
||||||
|
const float MOUSE_EXTENTS_VERT_ANGULAR_SIZE = 170.0f; // 5deg from poles
|
||||||
|
const float MOUSE_EXTENTS_VERT_PIXELS = VIRTUAL_SCREEN_SIZE_Y * (MOUSE_EXTENTS_VERT_ANGULAR_SIZE / DEFAULT_HMD_UI_VERT_ANGULAR_SIZE);
|
||||||
|
const float MOUSE_EXTENTS_HORZ_ANGULAR_SIZE = 360.0f; // full sphere
|
||||||
|
const float MOUSE_EXTENTS_HORZ_PIXELS = VIRTUAL_SCREEN_SIZE_X * (MOUSE_EXTENTS_HORZ_ANGULAR_SIZE / DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE);
|
||||||
|
|
||||||
|
glm::vec2 maxOverlayPosition = qApp->getUiSize();
|
||||||
|
float extaPixelsX = (MOUSE_EXTENTS_HORZ_PIXELS - maxOverlayPosition.x) / 2.0f;
|
||||||
|
float extaPixelsY = (MOUSE_EXTENTS_VERT_PIXELS - maxOverlayPosition.y) / 2.0f;
|
||||||
|
glm::vec2 mouseExtra { extaPixelsX, extaPixelsY };
|
||||||
|
glm::vec2 minMouse = vec2(0) - mouseExtra;
|
||||||
|
glm::vec2 maxMouse = maxOverlayPosition + mouseExtra;
|
||||||
|
|
||||||
|
_reticlePositionInHMD = glm::clamp(position, minMouse, maxMouse);
|
||||||
|
|
||||||
|
if (sendFakeEvent) {
|
||||||
|
// in HMD mode we need to fake our mouse moves...
|
||||||
|
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||||
|
auto button = Qt::NoButton;
|
||||||
|
auto buttons = QApplication::mouseButtons();
|
||||||
|
auto modifiers = QApplication::keyboardModifiers();
|
||||||
|
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
|
||||||
|
qApp->fakeMouseEvent(&event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||||
|
// remove it after we're done
|
||||||
|
const float REASONABLE_CHANGE = 50.0f;
|
||||||
|
glm::vec2 oldPos = toGlm(QCursor::pos());
|
||||||
|
auto distance = glm::distance(oldPos, position);
|
||||||
|
if (distance > REASONABLE_CHANGE) {
|
||||||
|
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCursor::setPos(position.x, position.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
|
||||||
|
glm::vec2 ApplicationCompositor::getReticleMaximumPosition() const {
|
||||||
|
glm::vec2 result;
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
result = glm::vec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y);
|
||||||
|
} else {
|
||||||
|
QRect rec = QApplication::desktop()->screenGeometry();
|
||||||
|
result = glm::vec2(rec.right(), rec.bottom());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
||||||
|
auto surfacePointAt = sphereSurfaceFromOverlay(cursorPos); // in world space
|
||||||
|
glm::vec3 worldSpaceCameraPosition = qApp->getCamera()->getPosition();
|
||||||
|
origin = worldSpaceCameraPosition;
|
||||||
|
direction = glm::normalize(surfacePointAt - worldSpaceCameraPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Finds the collision point of a world space ray
|
//Finds the collision point of a world space ray
|
||||||
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||||
|
auto headPose = qApp->getHMDSensorPose();
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
|
||||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
|
||||||
|
|
||||||
auto myCamera = qApp->getCamera();
|
auto myCamera = qApp->getCamera();
|
||||||
mat4 cameraMat = myCamera->getTransform();
|
mat4 cameraMat = myCamera->getTransform();
|
||||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||||
|
@ -459,25 +562,6 @@ void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
|
||||||
batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
|
batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.0f;
|
|
||||||
result /= MOUSE_RANGE;
|
|
||||||
result += 0.5f;
|
|
||||||
result *= qApp->getCanvasSize();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||||
glm::vec2 result = sphericalPos;
|
glm::vec2 result = sphericalPos;
|
||||||
result.x *= -1.0f;
|
result.x *= -1.0f;
|
||||||
|
@ -498,18 +582,8 @@ glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos
|
||||||
return result;
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const {
|
glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const {
|
||||||
|
auto headPose = qApp->getHMDSensorPose();
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
|
||||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
|
||||||
auto myCamera = qApp->getCamera();
|
auto myCamera = qApp->getCamera();
|
||||||
mat4 cameraMat = myCamera->getTransform();
|
mat4 cameraMat = myCamera->getTransform();
|
||||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||||
|
@ -517,12 +591,24 @@ glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& spher
|
||||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||||
auto center = vec3(0); // center of HUD in HUD space
|
auto center = vec3(0); // center of HUD in HUD space
|
||||||
auto direction = relativePosition - center; // direction to relative position in HUD space
|
auto direction = relativePosition - center; // direction to relative position in HUD space
|
||||||
|
|
||||||
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
|
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
|
||||||
auto overlayPos = sphericalToOverlay(polar);
|
auto overlayPos = sphericalToOverlay(polar);
|
||||||
return overlayPos;
|
return overlayPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 ApplicationCompositor::sphereSurfaceFromOverlay(const glm::vec2& overlay) const {
|
||||||
|
auto spherical = overlayToSpherical(overlay);
|
||||||
|
auto sphereSurfacePoint = getPoint(spherical.x, spherical.y);
|
||||||
|
auto headPose = qApp->getHMDSensorPose();
|
||||||
|
auto myCamera = qApp->getCamera();
|
||||||
|
mat4 cameraMat = myCamera->getTransform();
|
||||||
|
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||||
|
auto position4 = UITransform * vec4(sphereSurfacePoint, 1);
|
||||||
|
auto position = vec3(position4) / position4.w;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ApplicationCompositor::updateTooltips() {
|
void ApplicationCompositor::updateTooltips() {
|
||||||
if (_hoverItemId != _noItemId) {
|
if (_hoverItemId != _noItemId) {
|
||||||
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||||
|
|
|
@ -9,9 +9,13 @@
|
||||||
#ifndef hifi_ApplicationCompositor_h
|
#ifndef hifi_ApplicationCompositor_h
|
||||||
#define hifi_ApplicationCompositor_h
|
#define hifi_ApplicationCompositor_h
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <QCursor>
|
||||||
|
#include <QMouseEvent>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPropertyAnimation>
|
#include <QPropertyAnimation>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <EntityItemID.h>
|
#include <EntityItemID.h>
|
||||||
#include <GeometryCache.h>
|
#include <GeometryCache.h>
|
||||||
|
@ -22,12 +26,18 @@
|
||||||
class Camera;
|
class Camera;
|
||||||
class PalmData;
|
class PalmData;
|
||||||
class RenderArgs;
|
class RenderArgs;
|
||||||
|
class ReticleInterface;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const float MAGNIFY_WIDTH = 220.0f;
|
const float MAGNIFY_WIDTH = 220.0f;
|
||||||
const float MAGNIFY_HEIGHT = 100.0f;
|
const float MAGNIFY_HEIGHT = 100.0f;
|
||||||
const float MAGNIFY_MULT = 2.0f;
|
const float MAGNIFY_MULT = 2.0f;
|
||||||
|
|
||||||
const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
|
const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV
|
||||||
|
const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV
|
||||||
|
const float DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE = 240.0f;
|
||||||
|
const float DEFAULT_HMD_UI_VERT_ANGULAR_SIZE = DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE * (float)VIRTUAL_SCREEN_SIZE_Y / (float)VIRTUAL_SCREEN_SIZE_X;
|
||||||
|
|
||||||
// Handles the drawing of the overlays to the screen
|
// Handles the drawing of the overlays to the screen
|
||||||
// TODO, move divide up the rendering, displaying and input handling
|
// TODO, move divide up the rendering, displaying and input handling
|
||||||
|
@ -50,19 +60,15 @@ public:
|
||||||
|
|
||||||
// Converter from one frame of reference to another.
|
// Converter from one frame of reference to another.
|
||||||
// Frame of reference:
|
// Frame of reference:
|
||||||
// Screen: Position on the screen (x,y)
|
|
||||||
// Spherical: Polar coordinates that gives the position on the sphere we project on (yaw,pitch)
|
// Spherical: Polar coordinates that gives the position on the sphere we project on (yaw,pitch)
|
||||||
// Overlay: Position on the overlay (x,y)
|
// 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 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||||
glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) 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;
|
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||||
uint32_t getOverlayTexture() const;
|
uint32_t getOverlayTexture() const;
|
||||||
|
|
||||||
glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
|
glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
|
||||||
|
glm::vec3 sphereSurfaceFromOverlay(const glm::vec2& overlay) const;
|
||||||
|
|
||||||
void setCameraBaseTransform(const Transform& transform) { _cameraBaseTransform = transform; }
|
void setCameraBaseTransform(const Transform& transform) { _cameraBaseTransform = transform; }
|
||||||
const Transform& getCameraBaseTransform() const { return _cameraBaseTransform; }
|
const Transform& getCameraBaseTransform() const { return _cameraBaseTransform; }
|
||||||
|
@ -77,10 +83,28 @@ public:
|
||||||
float getAlpha() const { return _alpha; }
|
float getAlpha() const { return _alpha; }
|
||||||
void setAlpha(float alpha) { _alpha = alpha; }
|
void setAlpha(float alpha) { _alpha = alpha; }
|
||||||
|
|
||||||
static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
|
bool getReticleVisible() { return _reticleVisible; }
|
||||||
static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
|
void setReticleVisible(bool visible) { _reticleVisible = visible; }
|
||||||
|
|
||||||
|
float getReticleDepth() { return _reticleDepth; }
|
||||||
|
void setReticleDepth(float depth) { _reticleDepth = depth; }
|
||||||
|
|
||||||
|
glm::vec2 getReticlePosition();
|
||||||
|
void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true);
|
||||||
|
|
||||||
|
glm::vec2 getReticleMaximumPosition() const;
|
||||||
|
|
||||||
|
ReticleInterface* getReticleInterface() { return _reticleInterface; }
|
||||||
|
|
||||||
|
/// return value - true means the caller should not process the event further
|
||||||
|
bool handleRealMouseMoveEvent(bool sendFakeEvent = true);
|
||||||
|
void handleLeaveEvent();
|
||||||
|
QPointF getMouseEventPosition(QMouseEvent* event);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool shouldCaptureMouse() const;
|
||||||
|
|
||||||
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
||||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||||
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
|
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
|
||||||
|
@ -94,8 +118,8 @@ private:
|
||||||
QString _hoverItemDescription;
|
QString _hoverItemDescription;
|
||||||
quint64 _hoverItemEnterUsecs { 0 };
|
quint64 _hoverItemEnterUsecs { 0 };
|
||||||
|
|
||||||
float _hmdUIAngularSize { DEFAULT_HMD_UI_ANGULAR_SIZE };
|
float _hmdUIAngularSize { DEFAULT_HMD_UI_VERT_ANGULAR_SIZE };
|
||||||
float _textureFov { glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) };
|
float _textureFov { glm::radians(DEFAULT_HMD_UI_VERT_ANGULAR_SIZE) };
|
||||||
float _textureAspectRatio { 1.0f };
|
float _textureAspectRatio { 1.0f };
|
||||||
int _hemiVerticesID { GeometryCache::UNKNOWN_ID };
|
int _hemiVerticesID { GeometryCache::UNKNOWN_ID };
|
||||||
|
|
||||||
|
@ -115,6 +139,47 @@ private:
|
||||||
Transform _cameraBaseTransform;
|
Transform _cameraBaseTransform;
|
||||||
|
|
||||||
std::unique_ptr<QPropertyAnimation> _alphaPropertyAnimation;
|
std::unique_ptr<QPropertyAnimation> _alphaPropertyAnimation;
|
||||||
|
|
||||||
|
std::atomic<bool> _reticleVisible { true };
|
||||||
|
std::atomic<float> _reticleDepth { 1.0f };
|
||||||
|
|
||||||
|
// NOTE: when the compositor is running in HMD mode, it will control the reticle position as a custom
|
||||||
|
// application specific position, when it's in desktop mode, the reticle position will simply move
|
||||||
|
// the system mouse.
|
||||||
|
glm::vec2 _reticlePositionInHMD { 0.0f, 0.0f };
|
||||||
|
mutable QMutex _reticlePositionInHMDLock{ QMutex::Recursive };
|
||||||
|
|
||||||
|
QPointF _lastKnownRealMouse;
|
||||||
|
bool _ignoreMouseMove { false };
|
||||||
|
|
||||||
|
ReticleInterface* _reticleInterface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Scripting interface available to control the Reticle
|
||||||
|
class ReticleInterface : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||||
|
Q_PROPERTY(bool visible READ getVisible WRITE setVisible)
|
||||||
|
Q_PROPERTY(float depth READ getDepth WRITE setDepth)
|
||||||
|
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
|
||||||
|
|
||||||
|
Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
|
||||||
|
Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); }
|
||||||
|
|
||||||
|
Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
|
||||||
|
Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
|
||||||
|
|
||||||
|
Q_INVOKABLE glm::vec2 getPosition() { return _compositor->getReticlePosition(); }
|
||||||
|
Q_INVOKABLE void setPosition(glm::vec2 position) { _compositor->setReticlePosition(position); }
|
||||||
|
|
||||||
|
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
|
||||||
|
private:
|
||||||
|
ApplicationCompositor* _compositor;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_ApplicationCompositor_h
|
#endif // hifi_ApplicationCompositor_h
|
||||||
|
|
|
@ -251,12 +251,9 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
|
||||||
void ApplicationOverlay::buildFramebufferObject() {
|
void ApplicationOverlay::buildFramebufferObject() {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
|
||||||
QSize desiredSize = qApp->getDeviceSize();
|
auto uiSize = qApp->getUiSize();
|
||||||
int currentWidth = _overlayFramebuffer ? _overlayFramebuffer->getWidth() : 0;
|
|
||||||
int currentHeight = _overlayFramebuffer ? _overlayFramebuffer->getHeight() : 0;
|
if (_overlayFramebuffer && uiSize == _overlayFramebuffer->getSize()) {
|
||||||
QSize frameBufferCurrentSize(currentWidth, currentHeight);
|
|
||||||
|
|
||||||
if (_overlayFramebuffer && desiredSize == frameBufferCurrentSize) {
|
|
||||||
// Already built
|
// Already built
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -270,8 +267,8 @@ void ApplicationOverlay::buildFramebufferObject() {
|
||||||
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||||
|
|
||||||
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||||
auto width = desiredSize.width();
|
auto width = uiSize.x;
|
||||||
auto height = desiredSize.height();
|
auto height = uiSize.y;
|
||||||
|
|
||||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||||
_overlayColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
_overlayColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <gpu/Texture.h>
|
#include <gpu/Texture.h>
|
||||||
|
|
||||||
|
|
||||||
// Handles the drawing of the overlays to the screen
|
// Handles the drawing of the overlays to the screen
|
||||||
// TODO, move divide up the rendering, displaying and input handling
|
// TODO, move divide up the rendering, displaying and input handling
|
||||||
// facilities of this class
|
// facilities of this class
|
||||||
|
|
|
@ -87,13 +87,6 @@ void setupPreferences() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QString LOD_TUNING("Level of Detail Tuning");
|
static const QString LOD_TUNING("Level of Detail Tuning");
|
||||||
CheckPreference* acuityToggle;
|
|
||||||
{
|
|
||||||
auto getter = []()->bool { return DependencyManager::get<LODManager>()->getUseAcuity(); };
|
|
||||||
auto setter = [](bool value) { DependencyManager::get<LODManager>()->setUseAcuity(value); };
|
|
||||||
preferences->addPreference(acuityToggle = new CheckPreference(LOD_TUNING, "Render based on visual acuity", getter, setter));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = []()->float { return DependencyManager::get<LODManager>()->getDesktopLODDecreaseFPS(); };
|
auto getter = []()->float { return DependencyManager::get<LODManager>()->getDesktopLODDecreaseFPS(); };
|
||||||
auto setter = [](float value) { DependencyManager::get<LODManager>()->setDesktopLODDecreaseFPS(value); };
|
auto setter = [](float value) { DependencyManager::get<LODManager>()->setDesktopLODDecreaseFPS(value); };
|
||||||
|
@ -101,7 +94,6 @@ void setupPreferences() {
|
||||||
preference->setMin(0);
|
preference->setMin(0);
|
||||||
preference->setMax(120);
|
preference->setMax(120);
|
||||||
preference->setStep(1);
|
preference->setStep(1);
|
||||||
preference->setEnabler(acuityToggle);
|
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,18 +104,6 @@ void setupPreferences() {
|
||||||
preference->setMin(0);
|
preference->setMin(0);
|
||||||
preference->setMax(120);
|
preference->setMax(120);
|
||||||
preference->setStep(1);
|
preference->setStep(1);
|
||||||
preference->setEnabler(acuityToggle);
|
|
||||||
preferences->addPreference(preference);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto getter = []()->float { return 1.0f / DependencyManager::get<LODManager>()->getRenderDistanceInverseHighLimit(); };
|
|
||||||
auto setter = [](float value) { DependencyManager::get<LODManager>()->setRenderDistanceInverseHighLimit(1.0f / value); };
|
|
||||||
auto preference = new SpinnerPreference(LOD_TUNING, "Minimum Display Distance", getter, setter);
|
|
||||||
preference->setMin(5);
|
|
||||||
preference->setMax(32768);
|
|
||||||
preference->setStep(1);
|
|
||||||
preference->setEnabler(acuityToggle, true);
|
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +254,7 @@ void setupPreferences() {
|
||||||
{
|
{
|
||||||
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputBufferSize(); };
|
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputBufferSize(); };
|
||||||
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputBufferSize(value); };
|
auto setter = [](float value) { DependencyManager::get<AudioClient>()->setOutputBufferSize(value); };
|
||||||
auto preference = new SpinnerPreference(AUDIO, "Output Buffer Size (frames)", getter, setter);
|
auto preference = new SpinnerPreference(AUDIO, "Output Buffer Initial Size (frames)", getter, setter);
|
||||||
preference->setMin(1);
|
preference->setMin(1);
|
||||||
preference->setMax(20);
|
preference->setMax(20);
|
||||||
preference->setStep(1);
|
preference->setStep(1);
|
||||||
|
|
|
@ -283,9 +283,7 @@ void Stats::updateStats(bool force) {
|
||||||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||||
// LOD Details
|
// LOD Details
|
||||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||||
STAT_UPDATE(lodStatsRenderText, DependencyManager::get<LODManager>()->getLODStatsRenderText());
|
|
||||||
}
|
}
|
||||||
STAT_UPDATE(showAcuity, (_expanded || force) && DependencyManager::get<LODManager>()->getUseAcuity());
|
|
||||||
|
|
||||||
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
||||||
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||||
|
|
|
@ -30,7 +30,6 @@ class Stats : public QQuickItem {
|
||||||
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
||||||
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
|
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
|
||||||
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
||||||
Q_PROPERTY(bool showAcuity READ getShowAcuity WRITE setShowAcuity NOTIFY showAcuityChanged)
|
|
||||||
|
|
||||||
STATS_PROPERTY(int, serverCount, 0)
|
STATS_PROPERTY(int, serverCount, 0)
|
||||||
STATS_PROPERTY(int, renderrate, 0)
|
STATS_PROPERTY(int, renderrate, 0)
|
||||||
|
@ -80,7 +79,6 @@ class Stats : public QQuickItem {
|
||||||
STATS_PROPERTY(QString, packetStats, QString())
|
STATS_PROPERTY(QString, packetStats, QString())
|
||||||
STATS_PROPERTY(QString, lodStatus, QString())
|
STATS_PROPERTY(QString, lodStatus, QString())
|
||||||
STATS_PROPERTY(QString, timingStats, QString())
|
STATS_PROPERTY(QString, timingStats, QString())
|
||||||
STATS_PROPERTY(QString, lodStatsRenderText, QString())
|
|
||||||
STATS_PROPERTY(int, serverElements, 0)
|
STATS_PROPERTY(int, serverElements, 0)
|
||||||
STATS_PROPERTY(int, serverInternal, 0)
|
STATS_PROPERTY(int, serverInternal, 0)
|
||||||
STATS_PROPERTY(int, serverLeaves, 0)
|
STATS_PROPERTY(int, serverLeaves, 0)
|
||||||
|
@ -112,15 +110,12 @@ public:
|
||||||
emit expandedChanged();
|
emit expandedChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool getShowAcuity() { return _showAcuity; }
|
|
||||||
void setShowAcuity(bool newValue) { _showAcuity = newValue; }
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void forceUpdateStats() { updateStats(true); }
|
void forceUpdateStats() { updateStats(true); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void expandedChanged();
|
void expandedChanged();
|
||||||
void showAcuityChanged();
|
|
||||||
void timingExpandedChanged();
|
void timingExpandedChanged();
|
||||||
void serverCountChanged();
|
void serverCountChanged();
|
||||||
void renderrateChanged();
|
void renderrateChanged();
|
||||||
|
@ -128,7 +123,6 @@ signals:
|
||||||
void simrateChanged();
|
void simrateChanged();
|
||||||
void avatarSimrateChanged();
|
void avatarSimrateChanged();
|
||||||
void avatarCountChanged();
|
void avatarCountChanged();
|
||||||
void lodStatsRenderTextChanged();
|
|
||||||
void packetInCountChanged();
|
void packetInCountChanged();
|
||||||
void packetOutCountChanged();
|
void packetOutCountChanged();
|
||||||
void mbpsInChanged();
|
void mbpsInChanged();
|
||||||
|
@ -182,7 +176,6 @@ private:
|
||||||
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
||||||
bool _resetRecentMaxPacketsSoon{ true };
|
bool _resetRecentMaxPacketsSoon{ true };
|
||||||
bool _expanded{ false };
|
bool _expanded{ false };
|
||||||
bool _showAcuity{ false };
|
|
||||||
bool _timingExpanded{ false };
|
bool _timingExpanded{ false };
|
||||||
QString _monospaceFont;
|
QString _monospaceFont;
|
||||||
const AudioIOStats* _audioStats;
|
const AudioIOStats* _audioStats;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include <RenderDeferredTask.h>
|
#include <RenderDeferredTask.h>
|
||||||
#include <TextRenderer3D.h>
|
#include <TextRenderer3D.h>
|
||||||
|
|
||||||
const float DEFAULT_MARGIN = 0.1f;
|
|
||||||
const int FIXED_FONT_POINT_SIZE = 40;
|
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 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;
|
const float LINE_SCALE_RATIO = 1.2f;
|
||||||
|
|
|
@ -80,8 +80,9 @@ AudioClient::AudioClient() :
|
||||||
_isStereoInput(false),
|
_isStereoInput(false),
|
||||||
_outputStarveDetectionStartTimeMsec(0),
|
_outputStarveDetectionStartTimeMsec(0),
|
||||||
_outputStarveDetectionCount(0),
|
_outputStarveDetectionCount(0),
|
||||||
_outputBufferSizeFrames("audioOutputBufferSize", DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES),
|
_outputBufferSizeFrames("audioOutputBufferSizeFrames", DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES),
|
||||||
_outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled",
|
_sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()),
|
||||||
|
_outputStarveDetectionEnabled("audioOutputBufferStarveDetectionEnabled",
|
||||||
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED),
|
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED),
|
||||||
_outputStarveDetectionPeriodMsec("audioOutputStarveDetectionPeriod",
|
_outputStarveDetectionPeriodMsec("audioOutputStarveDetectionPeriod",
|
||||||
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD),
|
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD),
|
||||||
|
@ -109,6 +110,7 @@ AudioClient::AudioClient() :
|
||||||
|
|
||||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||||
|
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { switchOutputToAudioDevice(outputDeviceInfo); });
|
||||||
|
|
||||||
_inputDevices = getDeviceNames(QAudio::AudioInput);
|
_inputDevices = getDeviceNames(QAudio::AudioInput);
|
||||||
_outputDevices = getDeviceNames(QAudio::AudioOutput);
|
_outputDevices = getDeviceNames(QAudio::AudioOutput);
|
||||||
|
@ -277,9 +279,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
||||||
bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||||
const QAudioFormat& desiredAudioFormat,
|
const QAudioFormat& desiredAudioFormat,
|
||||||
QAudioFormat& adjustedAudioFormat) {
|
QAudioFormat& adjustedAudioFormat) {
|
||||||
// FIXME: directly using 24khz has a bug somewhere that causes channels to be swapped.
|
// There had been a note here that 2khz was swapping channels. That doesn't seem to be happening
|
||||||
// Continue using our internal resampler, for now.
|
// any more for me. If it does, then we'll want to always resample.
|
||||||
if (true || !audioDevice.isFormatSupported(desiredAudioFormat)) {
|
if (!audioDevice.isFormatSupported(desiredAudioFormat)) {
|
||||||
qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat;
|
qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat;
|
||||||
qCDebug(audioclient, "The desired audio format is not supported by this device");
|
qCDebug(audioclient, "The desired audio format is not supported by this device");
|
||||||
|
|
||||||
|
@ -287,7 +289,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||||
adjustedAudioFormat = desiredAudioFormat;
|
adjustedAudioFormat = desiredAudioFormat;
|
||||||
adjustedAudioFormat.setChannelCount(2);
|
adjustedAudioFormat.setChannelCount(2);
|
||||||
|
|
||||||
if (false && audioDevice.isFormatSupported(adjustedAudioFormat)) {
|
if (audioDevice.isFormatSupported(adjustedAudioFormat)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
adjustedAudioFormat.setChannelCount(1);
|
adjustedAudioFormat.setChannelCount(1);
|
||||||
|
@ -971,10 +973,8 @@ void AudioClient::outputNotify() {
|
||||||
_outputStarveDetectionStartTimeMsec = now;
|
_outputStarveDetectionStartTimeMsec = now;
|
||||||
_outputStarveDetectionCount = 0;
|
_outputStarveDetectionCount = 0;
|
||||||
|
|
||||||
int oldOutputBufferSizeFrames = _outputBufferSizeFrames.get();
|
int oldOutputBufferSizeFrames = _sessionOutputBufferSizeFrames;
|
||||||
int newOutputBufferSizeFrames = oldOutputBufferSizeFrames + 1;
|
int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false);
|
||||||
setOutputBufferSize(newOutputBufferSizeFrames);
|
|
||||||
newOutputBufferSizeFrames = _outputBufferSizeFrames.get();
|
|
||||||
if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) {
|
if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) {
|
||||||
qCDebug(audioclient) << "Starve detection threshold met, increasing buffer size to " << newOutputBufferSizeFrames;
|
qCDebug(audioclient) << "Starve detection threshold met, increasing buffer size to " << newOutputBufferSizeFrames;
|
||||||
}
|
}
|
||||||
|
@ -1038,15 +1038,19 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
||||||
|
|
||||||
// setup our general output device for audio-mixer audio
|
// setup our general output device for audio-mixer audio
|
||||||
_audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
_audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||||
_audioOutput->setBufferSize(_outputBufferSizeFrames.get() * _outputFrameSize * sizeof(int16_t));
|
int osDefaultBufferSize = _audioOutput->bufferSize();
|
||||||
|
int requestedSize = _sessionOutputBufferSizeFrames *_outputFrameSize * sizeof(int16_t);
|
||||||
|
_audioOutput->setBufferSize(requestedSize);
|
||||||
|
|
||||||
connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify);
|
connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify);
|
||||||
|
|
||||||
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize;
|
|
||||||
|
|
||||||
_audioOutputIODevice.start();
|
_audioOutputIODevice.start();
|
||||||
_audioOutput->start(&_audioOutputIODevice);
|
_audioOutput->start(&_audioOutputIODevice);
|
||||||
|
|
||||||
|
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize <<
|
||||||
|
"requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() <<
|
||||||
|
"os default:" << osDefaultBufferSize << "period size:" << _audioOutput->periodSize();
|
||||||
|
|
||||||
// setup a loopback audio output device
|
// setup a loopback audio output device
|
||||||
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||||
|
|
||||||
|
@ -1060,19 +1064,23 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
||||||
return supportedFormat;
|
return supportedFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::setOutputBufferSize(int numFrames) {
|
int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
|
||||||
numFrames = std::min(std::max(numFrames, MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES), MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES);
|
numFrames = std::min(std::max(numFrames, MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES), MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES);
|
||||||
if (numFrames != _outputBufferSizeFrames.get()) {
|
if (numFrames != _sessionOutputBufferSizeFrames) {
|
||||||
qCDebug(audioclient) << "Audio output buffer size (frames): " << numFrames;
|
qCDebug(audioclient) << "Audio output buffer size (frames): " << numFrames;
|
||||||
_outputBufferSizeFrames.set(numFrames);
|
_sessionOutputBufferSizeFrames = numFrames;
|
||||||
|
if (persist) {
|
||||||
|
_outputBufferSizeFrames.set(numFrames);
|
||||||
|
}
|
||||||
|
|
||||||
if (_audioOutput) {
|
if (_audioOutput) {
|
||||||
// The buffer size can't be adjusted after QAudioOutput::start() has been called, so
|
// The buffer size can't be adjusted after QAudioOutput::start() has been called, so
|
||||||
// recreate the device by switching to the default.
|
// recreate the device by switching to the default.
|
||||||
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
||||||
switchOutputToAudioDevice(outputDeviceInfo);
|
emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return numFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following constant is operating system dependent due to differences in
|
// The following constant is operating system dependent due to differences in
|
||||||
|
@ -1143,6 +1151,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
|
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
|
||||||
|
if (!bytesAudioOutputUnplayed) {
|
||||||
|
qCDebug(audioclient) << "empty audio buffer";
|
||||||
|
}
|
||||||
if (bytesAudioOutputUnplayed == 0 && bytesWritten == 0) {
|
if (bytesAudioOutputUnplayed == 0 && bytesWritten == 0) {
|
||||||
_unfulfilledReads++;
|
_unfulfilledReads++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,11 +57,7 @@ static const int NUM_AUDIO_CHANNELS = 2;
|
||||||
static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3;
|
static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3;
|
||||||
static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1;
|
static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1;
|
||||||
static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20;
|
static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20;
|
||||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
|
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
|
||||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false;
|
|
||||||
#else
|
|
||||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
|
|
||||||
#endif
|
|
||||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_THRESHOLD = 3;
|
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_THRESHOLD = 3;
|
||||||
static const quint64 DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds
|
static const quint64 DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds
|
||||||
|
|
||||||
|
@ -156,7 +152,7 @@ public slots:
|
||||||
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
|
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
|
||||||
void sendMuteEnvironmentPacket();
|
void sendMuteEnvironmentPacket();
|
||||||
|
|
||||||
void setOutputBufferSize(int numFrames);
|
int setOutputBufferSize(int numFrames, bool persist = true);
|
||||||
|
|
||||||
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector);
|
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector);
|
||||||
|
|
||||||
|
@ -184,6 +180,7 @@ signals:
|
||||||
void outputBytesToNetwork(int numBytes);
|
void outputBytesToNetwork(int numBytes);
|
||||||
void inputBytesFromNetwork(int numBytes);
|
void inputBytesFromNetwork(int numBytes);
|
||||||
|
|
||||||
|
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo);
|
||||||
void deviceChanged();
|
void deviceChanged();
|
||||||
|
|
||||||
void receivedFirstPacket();
|
void receivedFirstPacket();
|
||||||
|
@ -230,6 +227,7 @@ private:
|
||||||
int _outputStarveDetectionCount;
|
int _outputStarveDetectionCount;
|
||||||
|
|
||||||
Setting::Handle<int> _outputBufferSizeFrames;
|
Setting::Handle<int> _outputBufferSizeFrames;
|
||||||
|
int _sessionOutputBufferSizeFrames;
|
||||||
Setting::Handle<bool> _outputStarveDetectionEnabled;
|
Setting::Handle<bool> _outputStarveDetectionEnabled;
|
||||||
Setting::Handle<int> _outputStarveDetectionPeriodMsec;
|
Setting::Handle<int> _outputStarveDetectionPeriodMsec;
|
||||||
// Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size
|
// Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size
|
||||||
|
|
|
@ -106,71 +106,6 @@ namespace controller {
|
||||||
return getPoseValue(Input(device, source, ChannelType::POSE).getID());
|
return getPoseValue(Input(device, source, ChannelType::POSE).getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
//bool ScriptingInterface::isPrimaryButtonPressed() const {
|
|
||||||
// return isButtonPressed(StandardButtonChannel::A);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const {
|
|
||||||
// return getJoystickPosition(0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//int ScriptingInterface::getNumberOfButtons() const {
|
|
||||||
// return StandardButtonChannel::NUM_STANDARD_BUTTONS;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool ScriptingInterface::isButtonPressed(int buttonIndex) const {
|
|
||||||
// return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//int ScriptingInterface::getNumberOfTriggers() const {
|
|
||||||
// return StandardCounts::TRIGGERS;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//float ScriptingInterface::getTriggerValue(int triggerIndex) const {
|
|
||||||
// return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//int ScriptingInterface::getNumberOfJoysticks() const {
|
|
||||||
// return StandardCounts::ANALOG_STICKS;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const {
|
|
||||||
// StandardAxisChannel xid = StandardAxisChannel::LX;
|
|
||||||
// StandardAxisChannel yid = StandardAxisChannel::LY;
|
|
||||||
// if (joystickIndex != 0) {
|
|
||||||
// xid = StandardAxisChannel::RX;
|
|
||||||
// yid = StandardAxisChannel::RY;
|
|
||||||
// }
|
|
||||||
// vec2 result;
|
|
||||||
// result.x = getAxisValue(xid);
|
|
||||||
// result.y = getAxisValue(yid);
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//int ScriptingInterface::getNumberOfSpatialControls() const {
|
|
||||||
// return StandardCounts::POSES;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const {
|
|
||||||
// // FIXME extract the position from the standard pose
|
|
||||||
// return vec3();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
|
|
||||||
// // FIXME extract the velocity from the standard pose
|
|
||||||
// return vec3();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const {
|
|
||||||
// // FIXME extract the normal from the standard pose
|
|
||||||
// return vec3();
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
|
|
||||||
// // FIXME extract the rotation from the standard pose
|
|
||||||
// return quat();
|
|
||||||
//}
|
|
||||||
|
|
||||||
QVector<Action> ScriptingInterface::getAllActions() {
|
QVector<Action> ScriptingInterface::getAllActions() {
|
||||||
return DependencyManager::get<UserInputMapper>()->getAllActions();
|
return DependencyManager::get<UserInputMapper>()->getAllActions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,46 +90,6 @@ namespace controller {
|
||||||
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
||||||
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
|
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
|
||||||
|
|
||||||
Q_INVOKABLE bool getReticleVisible() { return _reticleVisible; }
|
|
||||||
Q_INVOKABLE void setReticleVisible(bool visible) { _reticleVisible = visible; }
|
|
||||||
|
|
||||||
Q_INVOKABLE float getReticleDepth() { return _reticleDepth; }
|
|
||||||
Q_INVOKABLE void setReticleDepth(float depth) { _reticleDepth = depth; }
|
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 getReticlePosition() {
|
|
||||||
return toGlm(QCursor::pos());
|
|
||||||
}
|
|
||||||
Q_INVOKABLE void setReticlePosition(glm::vec2 position) {
|
|
||||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
|
||||||
// remove it after we're done
|
|
||||||
const float REASONABLE_CHANGE = 50.0f;
|
|
||||||
glm::vec2 oldPos = toGlm(QCursor::pos());
|
|
||||||
auto distance = glm::distance(oldPos, position);
|
|
||||||
if (distance > REASONABLE_CHANGE) {
|
|
||||||
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCursor::setPos(position.x, position.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Q_INVOKABLE bool isPrimaryButtonPressed() const;
|
|
||||||
//Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
|
|
||||||
|
|
||||||
//Q_INVOKABLE int getNumberOfButtons() const;
|
|
||||||
//Q_INVOKABLE bool isButtonPressed(int buttonIndex) const;
|
|
||||||
|
|
||||||
//Q_INVOKABLE int getNumberOfTriggers() const;
|
|
||||||
//Q_INVOKABLE float getTriggerValue(int triggerIndex) const;
|
|
||||||
|
|
||||||
//Q_INVOKABLE int getNumberOfJoysticks() const;
|
|
||||||
//Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const;
|
|
||||||
|
|
||||||
//Q_INVOKABLE int getNumberOfSpatialControls() const;
|
|
||||||
//Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const;
|
|
||||||
//Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const;
|
|
||||||
//Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const;
|
|
||||||
//Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const;
|
|
||||||
|
|
||||||
Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; }
|
Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; }
|
||||||
Q_INVOKABLE const QVariantMap& getActions() { return _actions; }
|
Q_INVOKABLE const QVariantMap& getActions() { return _actions; }
|
||||||
Q_INVOKABLE const QVariantMap& getStandard() { return _standard; }
|
Q_INVOKABLE const QVariantMap& getStandard() { return _standard; }
|
||||||
|
@ -170,9 +130,6 @@ namespace controller {
|
||||||
bool _touchCaptured{ false };
|
bool _touchCaptured{ false };
|
||||||
bool _wheelCaptured{ false };
|
bool _wheelCaptured{ false };
|
||||||
bool _actionsCaptured{ false };
|
bool _actionsCaptured{ false };
|
||||||
|
|
||||||
bool _reticleVisible{ true };
|
|
||||||
float _reticleDepth { 1.0f };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,13 @@ public:
|
||||||
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; }
|
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; }
|
||||||
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override final { return _eyeProjections[eye]; }
|
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override final { return _eyeProjections[eye]; }
|
||||||
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
|
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
|
||||||
glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); }
|
glm::uvec2 getRecommendedUiSize() const override final {
|
||||||
|
// FIXME - would be good to have these values sync with ApplicationCompositor in a better way.
|
||||||
|
const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV
|
||||||
|
const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV
|
||||||
|
auto result = uvec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
|
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
|
||||||
void activate() override;
|
void activate() override;
|
||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
|
|
|
@ -492,16 +492,16 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
||||||
|
|
||||||
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
|
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
|
||||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId){
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event){
|
||||||
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event));
|
||||||
});
|
});
|
||||||
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
|
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
|
||||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||||
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event));
|
||||||
});
|
});
|
||||||
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
|
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
|
||||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||||
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event));
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
||||||
|
@ -519,7 +519,7 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
|
||||||
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
||||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
// process these events.
|
// process these events.
|
||||||
if (!_tree || _shuttingDown) {
|
if (!_tree || _shuttingDown) {
|
||||||
|
@ -540,20 +540,20 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit mousePressOnEntity(rayPickResult, event, deviceID);
|
emit mousePressOnEntity(rayPickResult, event);
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event));
|
||||||
|
|
||||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event));
|
||||||
} else {
|
} else {
|
||||||
emit mousePressOffEntity(rayPickResult, event, deviceID);
|
emit mousePressOffEntity(rayPickResult, event);
|
||||||
}
|
}
|
||||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
_lastMouseEvent = MouseEvent(*event);
|
||||||
_lastMouseEventValid = true;
|
_lastMouseEventValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
// process these events.
|
// process these events.
|
||||||
if (!_tree || _shuttingDown) {
|
if (!_tree || _shuttingDown) {
|
||||||
|
@ -565,24 +565,24 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mouseReleaseOnEntity(rayPickResult, event, deviceID);
|
emit mouseReleaseOnEntity(rayPickResult, event);
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
||||||
// we're releasing the button, then this is considered a clickOn event
|
// we're releasing the button, then this is considered a clickOn event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
_lastMouseEvent = MouseEvent(*event);
|
||||||
_lastMouseEventValid = true;
|
_lastMouseEventValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
||||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
// process these events.
|
// process these events.
|
||||||
if (!_tree || _shuttingDown) {
|
if (!_tree || _shuttingDown) {
|
||||||
|
@ -596,28 +596,28 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
|
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event));
|
||||||
|
|
||||||
// handle the hover logic...
|
// handle the hover logic...
|
||||||
|
|
||||||
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
||||||
// then we need to send the hover leave.
|
// then we need to send the hover leave.
|
||||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the new hover entity does not match the previous hover entity then we are entering the new one
|
// If the new hover entity does not match the previous hover entity then we are entering the new one
|
||||||
// this is true if the _currentHoverOverEntityID is known or unknown
|
// this is true if the _currentHoverOverEntityID is known or unknown
|
||||||
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
||||||
// we should send our hover over event
|
// we should send our hover over event
|
||||||
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event));
|
||||||
|
|
||||||
// remember what we're hovering over
|
// remember what we're hovering over
|
||||||
_currentHoverOverEntityID = rayPickResult.entityID;
|
_currentHoverOverEntityID = rayPickResult.entityID;
|
||||||
|
@ -627,8 +627,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
|
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
|
||||||
// send the hover leave for our previous entity
|
// send the hover leave for our previous entity
|
||||||
if (!_currentHoverOverEntityID.isInvalidID()) {
|
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event));
|
||||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -636,10 +636,10 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
||||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event, deviceID));
|
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event));
|
||||||
}
|
}
|
||||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
_lastMouseEvent = MouseEvent(*event);
|
||||||
_lastMouseEventValid = true;
|
_lastMouseEventValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,9 @@ public:
|
||||||
void deleteReleasedModels();
|
void deleteReleasedModels();
|
||||||
|
|
||||||
// event handles which may generate entity related events
|
// event handles which may generate entity related events
|
||||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
|
|
||||||
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
|
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
|
||||||
/// hovering over, and entering entities
|
/// hovering over, and entering entities
|
||||||
|
@ -86,10 +86,10 @@ public:
|
||||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||||
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||||
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||||
|
|
||||||
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
|
|
||||||
const float DPI = 30.47f;
|
const float DPI = 30.47f;
|
||||||
const float METERS_TO_INCHES = 39.3701f;
|
const float METERS_TO_INCHES = 39.3701f;
|
||||||
|
static uint32_t _currentWebCount { 0 };
|
||||||
|
// Don't allow more than 100 concurrent web views
|
||||||
|
static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100;
|
||||||
|
// If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer
|
||||||
|
static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||||
|
|
||||||
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
||||||
|
@ -41,28 +46,123 @@ RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemI
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
||||||
if (_webSurface) {
|
destroyWebSurface();
|
||||||
_webSurface->pause();
|
|
||||||
_webSurface->disconnect(_connection);
|
|
||||||
// The lifetime of the QML surface MUST be managed by the main thread
|
|
||||||
// Additionally, we MUST use local variables copied by value, rather than
|
|
||||||
// member variables, since they would implicitly refer to a this that
|
|
||||||
// is no longer valid
|
|
||||||
auto webSurface = _webSurface;
|
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
|
||||||
webSurface->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::disconnect(_mousePressConnection);
|
|
||||||
QObject::disconnect(_mouseReleaseConnection);
|
|
||||||
QObject::disconnect(_mouseMoveConnection);
|
|
||||||
QObject::disconnect(_hoverLeaveConnection);
|
|
||||||
qDebug() << "Destroyed web entity " << getID();
|
qDebug() << "Destroyed web entity " << getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
||||||
|
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
||||||
|
qWarning() << "Too many concurrent web views to create new view";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Building web surface";
|
||||||
|
++_currentWebCount;
|
||||||
|
// Save the original GL context, because creating a QML surface will create a new context
|
||||||
|
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||||
|
QSurface * currentSurface = currentContext->surface();
|
||||||
|
_webSurface = new OffscreenQmlSurface();
|
||||||
|
_webSurface->create(currentContext);
|
||||||
|
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||||
|
_webSurface->load("WebEntity.qml");
|
||||||
|
_webSurface->resume();
|
||||||
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
|
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
||||||
|
_texture = textureId;
|
||||||
|
});
|
||||||
|
// Restore the original GL context
|
||||||
|
currentContext->makeCurrent(currentSurface);
|
||||||
|
|
||||||
|
auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||||
|
// Ignore mouse interaction if we're locked
|
||||||
|
if (this->getLocked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->button() == Qt::MouseButton::RightButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||||
|
_lastPress = toGlm(mouseEvent->pos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersection.entityID == getID()) {
|
||||||
|
if (event->button() == Qt::MouseButton::RightButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
||||||
|
ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress);
|
||||||
|
if (!glm::any(glm::greaterThan(dist, ivec2(1)))) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||||
|
QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_lastPress = ivec2(INT_MIN);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME doesn't work... double click events not received
|
||||||
|
if (event->type() == QEvent::MouseButtonDblClick) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||||
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->button() == Qt::MouseButton::MiddleButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||||
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the intersection point to an actual offscreen pixel
|
||||||
|
glm::vec3 point = intersection.intersection;
|
||||||
|
point -= getPosition();
|
||||||
|
point = glm::inverse(getRotation()) * point;
|
||||||
|
point /= getDimensions();
|
||||||
|
point += 0.5f;
|
||||||
|
point.y = 1.0f - point.y;
|
||||||
|
point *= getDimensions() * METERS_TO_INCHES * DPI;
|
||||||
|
|
||||||
|
if (event->button() == Qt::MouseButton::LeftButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
this->_pressed = true;
|
||||||
|
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
||||||
|
} else if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
this->_pressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event->type() == QEvent::MouseMove) {
|
||||||
|
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward the mouse event.
|
||||||
|
QMouseEvent mappedEvent(event->type(),
|
||||||
|
QPoint((int)point.x, (int)point.y),
|
||||||
|
event->screenPos(), event->button(),
|
||||||
|
event->buttons(), event->modifiers());
|
||||||
|
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent);
|
||||||
|
_mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent);
|
||||||
|
_mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent);
|
||||||
|
_hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||||
|
if (this->_pressed && this->getID() == entityItemID) {
|
||||||
|
// If the user mouses off the entity while the button is down, simulate a mouse release
|
||||||
|
QMouseEvent mappedEvent(QEvent::MouseButtonRelease,
|
||||||
|
QPoint(_lastMove.x, _lastMove.y),
|
||||||
|
Qt::MouseButton::LeftButton,
|
||||||
|
Qt::MouseButtons(), Qt::KeyboardModifiers());
|
||||||
|
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RenderableWebEntityItem::render(RenderArgs* args) {
|
void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
|
|
||||||
#ifdef WANT_EXTRA_DEBUGGING
|
#ifdef WANT_EXTRA_DEBUGGING
|
||||||
{
|
{
|
||||||
gpu::Batch& batch = *args->_batch;
|
gpu::Batch& batch = *args->_batch;
|
||||||
|
@ -72,116 +172,19 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
|
||||||
QSurface * currentSurface = currentContext->surface();
|
|
||||||
if (!_webSurface) {
|
if (!_webSurface) {
|
||||||
_webSurface = new OffscreenQmlSurface();
|
if (!buildWebSurface(static_cast<EntityTreeRenderer*>(args->_renderer))) {
|
||||||
_webSurface->create(currentContext);
|
return;
|
||||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
}
|
||||||
_webSurface->load("WebEntity.qml");
|
|
||||||
_webSurface->resume();
|
|
||||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
|
||||||
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
|
||||||
_texture = textureId;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
|
||||||
// Ignore mouse interaction if we're locked
|
|
||||||
if (this->getLocked()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->button() == Qt::MouseButton::RightButton) {
|
|
||||||
if (event->type() == QEvent::MouseButtonPress) {
|
|
||||||
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
|
||||||
_lastPress = toGlm(mouseEvent->pos());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersection.entityID == getID()) {
|
|
||||||
if (event->button() == Qt::MouseButton::RightButton) {
|
|
||||||
if (event->type() == QEvent::MouseButtonRelease) {
|
|
||||||
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
|
|
||||||
ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress);
|
|
||||||
if (!glm::any(glm::greaterThan(dist, ivec2(1)))) {
|
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
|
||||||
QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_lastPress = ivec2(INT_MIN);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME doesn't work... double click events not received
|
|
||||||
if (event->type() == QEvent::MouseButtonDblClick) {
|
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
|
||||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->button() == Qt::MouseButton::MiddleButton) {
|
|
||||||
if (event->type() == QEvent::MouseButtonRelease) {
|
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
|
||||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the intersection point to an actual offscreen pixel
|
|
||||||
glm::vec3 point = intersection.intersection;
|
|
||||||
point -= getPosition();
|
|
||||||
point = glm::inverse(getRotation()) * point;
|
|
||||||
point /= getDimensions();
|
|
||||||
point += 0.5f;
|
|
||||||
point.y = 1.0f - point.y;
|
|
||||||
point *= getDimensions() * METERS_TO_INCHES * DPI;
|
|
||||||
|
|
||||||
if (event->button() == Qt::MouseButton::LeftButton) {
|
|
||||||
if (event->type() == QEvent::MouseButtonPress) {
|
|
||||||
this->_pressed = true;
|
|
||||||
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
|
||||||
} else if (event->type() == QEvent::MouseButtonRelease) {
|
|
||||||
this->_pressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event->type() == QEvent::MouseMove) {
|
|
||||||
this->_lastMove = ivec2((int)point.x, (int)point.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward the mouse event.
|
|
||||||
QMouseEvent mappedEvent(event->type(),
|
|
||||||
QPoint((int)point.x, (int)point.y),
|
|
||||||
event->screenPos(), event->button(),
|
|
||||||
event->buttons(), event->modifiers());
|
|
||||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
|
|
||||||
_mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent);
|
|
||||||
_mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent);
|
|
||||||
_mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent);
|
|
||||||
_hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
|
||||||
if (this->_pressed && this->getID() == entityItemID) {
|
|
||||||
// If the user mouses off the entity while the button is down, simulate a mouse release
|
|
||||||
QMouseEvent mappedEvent(QEvent::MouseButtonRelease,
|
|
||||||
QPoint(_lastMove.x, _lastMove.y),
|
|
||||||
Qt::MouseButton::LeftButton,
|
|
||||||
Qt::MouseButtons(), Qt::KeyboardModifiers());
|
|
||||||
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lastRenderTime = usecTimestampNow();
|
||||||
glm::vec2 dims = glm::vec2(getDimensions());
|
glm::vec2 dims = glm::vec2(getDimensions());
|
||||||
dims *= METERS_TO_INCHES * DPI;
|
dims *= METERS_TO_INCHES * DPI;
|
||||||
// The offscreen surface is idempotent for resizes (bails early
|
// The offscreen surface is idempotent for resizes (bails early
|
||||||
// if it's a no-op), so it's safe to just call resize every frame
|
// if it's a no-op), so it's safe to just call resize every frame
|
||||||
// without worrying about excessive overhead.
|
// without worrying about excessive overhead.
|
||||||
_webSurface->resize(QSize(dims.x, dims.y));
|
_webSurface->resize(QSize(dims.x, dims.y));
|
||||||
currentContext->makeCurrent(currentSurface);
|
|
||||||
|
|
||||||
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
|
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
|
||||||
Q_ASSERT(getType() == EntityTypes::Web);
|
Q_ASSERT(getType() == EntityTypes::Web);
|
||||||
|
@ -223,3 +226,37 @@ void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) {
|
||||||
QObject* RenderableWebEntityItem::getEventHandler() {
|
QObject* RenderableWebEntityItem::getEventHandler() {
|
||||||
return _webSurface->getEventHandler();
|
return _webSurface->getEventHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderableWebEntityItem::destroyWebSurface() {
|
||||||
|
if (_webSurface) {
|
||||||
|
--_currentWebCount;
|
||||||
|
_webSurface->pause();
|
||||||
|
_webSurface->disconnect(_connection);
|
||||||
|
QObject::disconnect(_mousePressConnection);
|
||||||
|
_mousePressConnection = QMetaObject::Connection();
|
||||||
|
QObject::disconnect(_mouseReleaseConnection);
|
||||||
|
_mouseReleaseConnection = QMetaObject::Connection();
|
||||||
|
QObject::disconnect(_mouseMoveConnection);
|
||||||
|
_mouseMoveConnection = QMetaObject::Connection();
|
||||||
|
QObject::disconnect(_hoverLeaveConnection);
|
||||||
|
_hoverLeaveConnection = QMetaObject::Connection();
|
||||||
|
|
||||||
|
// The lifetime of the QML surface MUST be managed by the main thread
|
||||||
|
// Additionally, we MUST use local variables copied by value, rather than
|
||||||
|
// member variables, since they would implicitly refer to a this that
|
||||||
|
// is no longer valid
|
||||||
|
auto webSurface = _webSurface;
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
||||||
|
webSurface->deleteLater();
|
||||||
|
});
|
||||||
|
_webSurface = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RenderableWebEntityItem::update(const quint64& now) {
|
||||||
|
auto interval = now - _lastRenderTime;
|
||||||
|
if (interval > MAX_NO_RENDER_INTERVAL) {
|
||||||
|
destroyWebSurface();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
class OffscreenQmlSurface;
|
class OffscreenQmlSurface;
|
||||||
class QWindow;
|
class QWindow;
|
||||||
class QObject;
|
class QObject;
|
||||||
|
class EntityTreeRenderer;
|
||||||
|
|
||||||
class RenderableWebEntityItem : public WebEntityItem {
|
class RenderableWebEntityItem : public WebEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -31,15 +32,22 @@ public:
|
||||||
void setProxyWindow(QWindow* proxyWindow);
|
void setProxyWindow(QWindow* proxyWindow);
|
||||||
QObject* getEventHandler();
|
QObject* getEventHandler();
|
||||||
|
|
||||||
|
void update(const quint64& now) override;
|
||||||
|
bool needsToCallUpdate() const { return _webSurface != nullptr; }
|
||||||
|
|
||||||
SIMPLE_RENDERABLE();
|
SIMPLE_RENDERABLE();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool buildWebSurface(EntityTreeRenderer* renderer);
|
||||||
|
void destroyWebSurface();
|
||||||
|
|
||||||
OffscreenQmlSurface* _webSurface{ nullptr };
|
OffscreenQmlSurface* _webSurface{ nullptr };
|
||||||
QMetaObject::Connection _connection;
|
QMetaObject::Connection _connection;
|
||||||
uint32_t _texture{ 0 };
|
uint32_t _texture{ 0 };
|
||||||
ivec2 _lastPress{ INT_MIN };
|
ivec2 _lastPress{ INT_MIN };
|
||||||
bool _pressed{ false };
|
bool _pressed{ false };
|
||||||
ivec2 _lastMove{ INT_MIN };
|
ivec2 _lastMove{ INT_MIN };
|
||||||
|
uint64_t _lastRenderTime{ 0 };
|
||||||
|
|
||||||
QMetaObject::Connection _mousePressConnection;
|
QMetaObject::Connection _mousePressConnection;
|
||||||
QMetaObject::Connection _mouseReleaseConnection;
|
QMetaObject::Connection _mouseReleaseConnection;
|
||||||
|
|
|
@ -103,6 +103,7 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID
|
||||||
// our non-pure virtual subclass for now...
|
// our non-pure virtual subclass for now...
|
||||||
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) :
|
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) :
|
||||||
EntityItem(entityItemID),
|
EntityItem(entityItemID),
|
||||||
|
_previousPosition(getPosition()),
|
||||||
_lastSimulated(usecTimestampNow())
|
_lastSimulated(usecTimestampNow())
|
||||||
{
|
{
|
||||||
_type = EntityTypes::ParticleEffect;
|
_type = EntityTypes::ParticleEffect;
|
||||||
|
@ -623,7 +624,8 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit a new particle at tail index.
|
// emit a new particle at tail index.
|
||||||
_particles.push_back(createParticle());
|
_particles.push_back(createParticle(glm::mix(_previousPosition, getPosition(),
|
||||||
|
(deltaTime - timeLeftInFrame) / deltaTime)));
|
||||||
auto particle = _particles.back();
|
auto particle = _particles.back();
|
||||||
particle.lifetime += timeLeftInFrame;
|
particle.lifetime += timeLeftInFrame;
|
||||||
|
|
||||||
|
@ -637,15 +639,16 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
||||||
|
|
||||||
_timeUntilNextEmit -= timeLeftInFrame;
|
_timeUntilNextEmit -= timeLeftInFrame;
|
||||||
}
|
}
|
||||||
|
_previousPosition = getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
|
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle(const glm::vec3& position) {
|
||||||
Particle particle;
|
Particle particle;
|
||||||
|
|
||||||
|
|
||||||
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
||||||
if (getEmitterShouldTrail()) {
|
if (getEmitterShouldTrail()) {
|
||||||
particle.position = getPosition();
|
particle.position = position;
|
||||||
}
|
}
|
||||||
// Position, velocity, and acceleration
|
// Position, velocity, and acceleration
|
||||||
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
||||||
|
|
|
@ -227,7 +227,7 @@ protected:
|
||||||
|
|
||||||
bool isAnimatingSomething() const;
|
bool isAnimatingSomething() const;
|
||||||
|
|
||||||
Particle createParticle();
|
Particle createParticle(const glm::vec3& position);
|
||||||
void stepSimulation(float deltaTime);
|
void stepSimulation(float deltaTime);
|
||||||
void integrateParticle(Particle& particle, float deltaTime);
|
void integrateParticle(Particle& particle, float deltaTime);
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ protected:
|
||||||
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
||||||
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
||||||
|
|
||||||
|
glm::vec3 _previousPosition;
|
||||||
quint64 _lastSimulated { 0 };
|
quint64 _lastSimulated { 0 };
|
||||||
bool _isEmitting { true };
|
bool _isEmitting { true };
|
||||||
|
|
||||||
|
|
|
@ -175,6 +175,7 @@ private:
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
|
|
||||||
getContextObject()->moveToThread(QCoreApplication::instance()->thread());
|
getContextObject()->moveToThread(QCoreApplication::instance()->thread());
|
||||||
|
_thread.quit();
|
||||||
_cond.wakeOne();
|
_cond.wakeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +229,7 @@ private:
|
||||||
|
|
||||||
_quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y));
|
_quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y));
|
||||||
|
|
||||||
{
|
try {
|
||||||
PROFILE_RANGE("qml_render")
|
PROFILE_RANGE("qml_render")
|
||||||
TexturePtr texture = _textures.getNextTexture();
|
TexturePtr texture = _textures.getNextTexture();
|
||||||
_fbo->Bind(Framebuffer::Target::Draw);
|
_fbo->Bind(Framebuffer::Target::Draw);
|
||||||
|
@ -245,8 +246,10 @@ private:
|
||||||
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
|
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
|
||||||
_quickWindow->resetOpenGLState();
|
_quickWindow->resetOpenGLState();
|
||||||
_escrow.submit(GetName(*texture));
|
_escrow.submit(GetName(*texture));
|
||||||
|
_lastRenderTime = usecTimestampNow();
|
||||||
|
} catch (std::runtime_error& error) {
|
||||||
|
qWarning() << "Failed to render QML " << error.what();
|
||||||
}
|
}
|
||||||
_lastRenderTime = usecTimestampNow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void aboutToQuit() {
|
void aboutToQuit() {
|
||||||
|
@ -321,7 +324,7 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
|
||||||
|
|
||||||
void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
_renderer = new OffscreenQmlRenderer(this, shareContext);
|
_renderer = new OffscreenQmlRenderer(this, shareContext);
|
||||||
|
_renderer->_renderControl->_renderWindow = _proxyWindow;
|
||||||
// Create a QML engine.
|
// Create a QML engine.
|
||||||
_qmlEngine = new QQmlEngine;
|
_qmlEngine = new QQmlEngine;
|
||||||
if (!_qmlEngine->incubationController()) {
|
if (!_qmlEngine->incubationController()) {
|
||||||
|
@ -610,7 +613,10 @@ bool OffscreenQmlSurface::isPaused() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
|
void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
|
||||||
_renderer->_renderControl->_renderWindow = window;
|
_proxyWindow = window;
|
||||||
|
if (_renderer && _renderer->_renderControl) {
|
||||||
|
_renderer->_renderControl->_renderWindow = window;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* OffscreenQmlSurface::getEventHandler() {
|
QObject* OffscreenQmlSurface::getEventHandler() {
|
||||||
|
|
|
@ -95,7 +95,7 @@ private:
|
||||||
bool _paused{ true };
|
bool _paused{ true };
|
||||||
uint8_t _maxFps{ 60 };
|
uint8_t _maxFps{ 60 };
|
||||||
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
||||||
|
QWindow* _proxyWindow { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -57,7 +57,7 @@ void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) {
|
||||||
_inputDevice->_buttonPressedMap.erase(input.getChannel());
|
_inputDevice->_buttonPressedMap.erase(input.getChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::MouseButton) event->button());
|
auto input = _inputDevice->makeInput((Qt::MouseButton) event->button());
|
||||||
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
||||||
if (!result.second) {
|
if (!result.second) {
|
||||||
|
@ -70,7 +70,7 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic
|
||||||
eraseMouseClicked();
|
eraseMouseClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::MouseButton) event->button());
|
auto input = _inputDevice->makeInput((Qt::MouseButton) event->button());
|
||||||
_inputDevice->_buttonPressedMap.erase(input.getChannel());
|
_inputDevice->_buttonPressedMap.erase(input.getChannel());
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ void KeyboardMouseDevice::eraseMouseClicked() {
|
||||||
_inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel());
|
_inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) {
|
||||||
QPoint currentPos = event->pos();
|
QPoint currentPos = event->pos();
|
||||||
QPoint currentMove = currentPos - _lastCursor;
|
QPoint currentMove = currentPos - _lastCursor;
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,9 @@ public:
|
||||||
void keyPressEvent(QKeyEvent* event);
|
void keyPressEvent(QKeyEvent* event);
|
||||||
void keyReleaseEvent(QKeyEvent* event);
|
void keyReleaseEvent(QKeyEvent* event);
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void eraseMouseClicked();
|
void eraseMouseClicked();
|
||||||
|
|
||||||
void touchBeginEvent(const QTouchEvent* event);
|
void touchBeginEvent(const QTouchEvent* event);
|
||||||
|
|
|
@ -92,15 +92,15 @@ void Model::setScale(const glm::vec3& scale) {
|
||||||
_scaledToFit = false;
|
_scaledToFit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float METERS_PER_MILLIMETER = 0.01f;
|
const float SCALE_CHANGE_EPSILON = 0.01f;
|
||||||
|
|
||||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||||
if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) {
|
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
|
||||||
_scale = scale;
|
_scale = scale;
|
||||||
if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) {
|
if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
initJointTransforms();
|
simulate(0.0f, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,9 @@ MouseEvent::MouseEvent() :
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MouseEvent::MouseEvent(const QMouseEvent& event, const unsigned int deviceID) :
|
MouseEvent::MouseEvent(const QMouseEvent& event) :
|
||||||
x(event.x()),
|
x(event.x()),
|
||||||
y(event.y()),
|
y(event.y()),
|
||||||
deviceID(deviceID),
|
|
||||||
isLeftButton(event.buttons().testFlag(Qt::LeftButton)),
|
isLeftButton(event.buttons().testFlag(Qt::LeftButton)),
|
||||||
isRightButton(event.buttons().testFlag(Qt::RightButton)),
|
isRightButton(event.buttons().testFlag(Qt::RightButton)),
|
||||||
isMiddleButton(event.buttons().testFlag(Qt::MiddleButton)),
|
isMiddleButton(event.buttons().testFlag(Qt::MiddleButton)),
|
||||||
|
@ -66,7 +65,6 @@ QScriptValue MouseEvent::toScriptValue(QScriptEngine* engine, const MouseEvent&
|
||||||
obj.setProperty("x", event.x);
|
obj.setProperty("x", event.x);
|
||||||
obj.setProperty("y", event.y);
|
obj.setProperty("y", event.y);
|
||||||
obj.setProperty("button", event.button);
|
obj.setProperty("button", event.button);
|
||||||
obj.setProperty("deviceID", event.deviceID);
|
|
||||||
obj.setProperty("isLeftButton", event.isLeftButton);
|
obj.setProperty("isLeftButton", event.isLeftButton);
|
||||||
obj.setProperty("isRightButton", event.isRightButton);
|
obj.setProperty("isRightButton", event.isRightButton);
|
||||||
obj.setProperty("isMiddleButton", event.isMiddleButton);
|
obj.setProperty("isMiddleButton", event.isMiddleButton);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
class MouseEvent {
|
class MouseEvent {
|
||||||
public:
|
public:
|
||||||
MouseEvent();
|
MouseEvent();
|
||||||
MouseEvent(const QMouseEvent& event, const unsigned int deviceID = 0);
|
MouseEvent(const QMouseEvent& event);
|
||||||
|
|
||||||
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||||
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
||||||
|
@ -26,7 +26,6 @@ public:
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
unsigned int deviceID;
|
|
||||||
QString button;
|
QString button;
|
||||||
bool isLeftButton;
|
bool isLeftButton;
|
||||||
bool isRightButton;
|
bool isRightButton;
|
||||||
|
|
|
@ -29,20 +29,6 @@ namespace Cursor {
|
||||||
Source getType() const {
|
Source getType() const {
|
||||||
return Source::MOUSE;
|
return Source::MOUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 getScreenPosition() const {
|
|
||||||
return toGlm(QCursor::pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
ivec2 getWindowPosition(QWidget* widget) const {
|
|
||||||
return toGlm(widget->mapFromGlobal(QCursor::pos()));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec2 getRelativePosition(QWidget* widget) const {
|
|
||||||
vec2 pos = getWindowPosition(widget);
|
|
||||||
pos /= vec2(toGlm(widget->size()));
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static QMap<uint16_t, QString> ICONS;
|
static QMap<uint16_t, QString> ICONS;
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
namespace Cursor {
|
namespace Cursor {
|
||||||
enum class Source {
|
enum class Source {
|
||||||
MOUSE,
|
MOUSE,
|
||||||
LEFT_HAND,
|
|
||||||
RIGHT_HAND,
|
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,9 +31,6 @@ namespace Cursor {
|
||||||
class Instance {
|
class Instance {
|
||||||
public:
|
public:
|
||||||
virtual Source getType() const = 0;
|
virtual Source getType() const = 0;
|
||||||
virtual ivec2 getWindowPosition(QWidget* widget) const = 0;
|
|
||||||
virtual vec2 getRelativePosition(QWidget* widget) const = 0;
|
|
||||||
virtual ivec2 getScreenPosition() const = 0;
|
|
||||||
virtual void setIcon(uint16_t icon);
|
virtual void setIcon(uint16_t icon);
|
||||||
virtual uint16_t getIcon() const;
|
virtual uint16_t getIcon() const;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -101,9 +101,16 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
||||||
float frameDuration = 1.f / displayFrequency;
|
float frameDuration = 1.f / displayFrequency;
|
||||||
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
||||||
|
|
||||||
|
#if THREADED_PRESENT
|
||||||
// TODO: this seems awfuly long, 44ms total, but it produced the best results.
|
// TODO: this seems awfuly long, 44ms total, but it produced the best results.
|
||||||
const float NUM_PREDICTION_FRAMES = 3.0f;
|
const float NUM_PREDICTION_FRAMES = 3.0f;
|
||||||
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
|
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
|
||||||
|
#else
|
||||||
|
uint64_t frameCounter;
|
||||||
|
float timeSinceLastVsync;
|
||||||
|
_system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter);
|
||||||
|
float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons;
|
||||||
|
#endif
|
||||||
|
|
||||||
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||||
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
|
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
|
||||||
|
@ -126,8 +133,6 @@ void OpenVrDisplayPlugin::internalPresent() {
|
||||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||||
|
|
||||||
glFinish();
|
|
||||||
|
|
||||||
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||||
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue