mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 02:57:00 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix/light-intensity
This commit is contained in:
commit
77ad4039bc
94 changed files with 1802 additions and 1659 deletions
94
examples/audioExamples/injectorLoadTest.js
Normal file
94
examples/audioExamples/injectorLoadTest.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// injectorLoadTest.js
|
||||
// audio
|
||||
//
|
||||
// Created by Eric Levin 2/1/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
|
||||
// This script tests what happens when many audio injectors are created and played
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
|
||||
var numSoundsToPlayPerBatch = 35 // Number of simultaneously playing sounds
|
||||
var numSoundsPlaying = 0;
|
||||
var timeBetweenBatch = 30000;
|
||||
// A green box represents an injector that is playing
|
||||
|
||||
var basePosition = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var soundBoxes = [];
|
||||
|
||||
var testSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/dove.wav");
|
||||
var totalInjectors = 0;
|
||||
var clipDuration;
|
||||
|
||||
if(!testSound.downloaded) {
|
||||
|
||||
print("SOUND IS NOT READY YET")
|
||||
testSound.ready.connect(function() {
|
||||
playSounds();
|
||||
clipDuration = testSound.duration;
|
||||
});
|
||||
} else {
|
||||
// otherwise play sounds right away
|
||||
playSounds();
|
||||
clipDuration = testSound.duration;
|
||||
}
|
||||
|
||||
function playSounds() {
|
||||
print("PLAY SOUNDS!")
|
||||
for (var i = 0; i < numSoundsToPlayPerBatch; i++) {
|
||||
playSound();
|
||||
}
|
||||
|
||||
Script.setTimeout(function() {
|
||||
numSoundsPlaying = 0;
|
||||
}, clipDuration);
|
||||
print("EBL Total Number of Injectors: " + totalInjectors);
|
||||
|
||||
Script.setTimeout(function() {
|
||||
playSounds();
|
||||
}, timeBetweenBatch);
|
||||
}
|
||||
|
||||
|
||||
function playSound() {
|
||||
var position = Vec3.sum(basePosition, {x: randFloat(-.1, .1), y: randFloat(-1, 1), z: randFloat(-3, -.1)});
|
||||
var injector = Audio.playSound(testSound, {
|
||||
position: position,
|
||||
volume: 0.2
|
||||
});
|
||||
|
||||
numSoundsPlaying++;
|
||||
print("NUM SOUNDS PLAYING: " + numSoundsPlaying);
|
||||
print("*******************************************");
|
||||
print("INJECTOR VALUE: ")
|
||||
print(JSON.stringify(injector));
|
||||
totalInjectors++;
|
||||
|
||||
var soundBox = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "Debug Sound Box",
|
||||
color: {red: 200, green: 10, blue: 200},
|
||||
dimensions: {x: 0.1, y: 0.1, z: 0.1},
|
||||
position: position
|
||||
});
|
||||
|
||||
soundBoxes.push(soundBox);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
soundBoxes.forEach( function(soundBox) {
|
||||
Entities.deleteEntity(soundBox);
|
||||
});
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -132,6 +132,7 @@ function maybeGoActive(event) {
|
|||
}
|
||||
}
|
||||
var wasHmdActive = false;
|
||||
var wasMouseCaptured = false;
|
||||
function maybeGoAway() {
|
||||
if (HMD.active !== wasHmdActive) {
|
||||
wasHmdActive = !wasHmdActive;
|
||||
|
@ -139,6 +140,17 @@ function maybeGoAway() {
|
|||
goAway();
|
||||
}
|
||||
}
|
||||
|
||||
// If the mouse has gone from captured, to non-captured state,
|
||||
// then it likely means the person is still in the HMD, but has
|
||||
// tabbed away from the application (meaning they don't have mouse
|
||||
// control) and they likely want to go into an away state
|
||||
if (Reticle.mouseCaptured !== wasMouseCaptured) {
|
||||
wasMouseCaptured = !wasMouseCaptured;
|
||||
if (!wasMouseCaptured) {
|
||||
goAway();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(maybeGoAway);
|
||||
|
|
|
@ -478,6 +478,25 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
this.searchIndicatorOn = function(handPosition, distantPickRay) {
|
||||
var SEARCH_SPHERE_SIZE = 0.011;
|
||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
||||
|
||||
if (this.intersectionDistance > 0) {
|
||||
// If we hit something with our pick ray, move the search sphere toward that distance
|
||||
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE +
|
||||
this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
|
||||
}
|
||||
|
||||
var searchSphereLocation = Vec3.sum(distantPickRay.origin,
|
||||
Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
|
||||
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance,
|
||||
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) {
|
||||
this.overlayLineOn(handPosition, searchSphereLocation,
|
||||
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
this.handleDistantParticleBeam = function(handPosition, objectPosition, color) {
|
||||
|
||||
|
@ -724,7 +743,7 @@ function MyController(hand) {
|
|||
}
|
||||
this.searchSphereOff();
|
||||
|
||||
Controller.setReticleVisible(true);
|
||||
Reticle.setVisible(true);
|
||||
|
||||
};
|
||||
|
||||
|
@ -921,7 +940,8 @@ function MyController(hand) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE && !near) {
|
||||
if (this.state == STATE_SEARCHING &&
|
||||
!isPhysical && distance > NEAR_PICK_MAX_DISTANCE && !near && !grabbableDataForCandidate.wantsTrigger) {
|
||||
// we can't distance-grab non-physical
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not physical and too far for near-grab");
|
||||
|
@ -1005,25 +1025,8 @@ function MyController(hand) {
|
|||
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
var SEARCH_SPHERE_SIZE = 0.011;
|
||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
||||
|
||||
if (this.intersectionDistance > 0) {
|
||||
// If we hit something with our pick ray, move the search sphere toward that distance
|
||||
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE +
|
||||
this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
|
||||
}
|
||||
|
||||
var searchSphereLocation = Vec3.sum(distantPickRay.origin,
|
||||
Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
|
||||
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance,
|
||||
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) {
|
||||
this.overlayLineOn(handPosition, searchSphereLocation,
|
||||
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
Controller.setReticleVisible(false);
|
||||
this.searchIndicatorOn(handPosition, distantPickRay);
|
||||
Reticle.setVisible(false);
|
||||
|
||||
};
|
||||
|
||||
|
@ -1543,17 +1546,20 @@ function MyController(hand) {
|
|||
var now = Date.now();
|
||||
if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
||||
var intersection = Entities.findRayIntersection(pickRay, true);
|
||||
this.lastPickTime = now;
|
||||
if (intersection.entityID != this.grabbedEntity) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
if (intersection.accurate) {
|
||||
this.lastPickTime = now;
|
||||
if (intersection.entityID != this.grabbedEntity) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
}
|
||||
if (intersection.intersects) {
|
||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
}
|
||||
this.searchIndicatorOn(handPosition, pickRay);
|
||||
}
|
||||
}
|
||||
|
||||
if (USE_ENTITY_LINES_FOR_MOVING === true) {
|
||||
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
this.callEntityMethodOnGrabbed("continueFarTrigger");
|
||||
};
|
||||
|
||||
|
@ -1581,48 +1587,43 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
ids.forEach(function(id) {
|
||||
|
||||
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
|
||||
if (props.name === 'pointer') {
|
||||
return;
|
||||
} else {
|
||||
var entityMinPoint = props.boundingBox.brn;
|
||||
var entityMaxPoint = props.boundingBox.tfl;
|
||||
var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint);
|
||||
var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint);
|
||||
|
||||
if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) {
|
||||
// we haven't been touched before, but either right or left is touching us now
|
||||
_this.allTouchedIDs[id] = true;
|
||||
_this.startTouch(id);
|
||||
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) {
|
||||
// we have been touched before and are still being touched
|
||||
// continue touch
|
||||
_this.continueTouch(id);
|
||||
} else if (_this.allTouchedIDs[id]) {
|
||||
delete _this.allTouchedIDs[id];
|
||||
_this.stopTouch(id);
|
||||
|
||||
} else {
|
||||
//we are in another state
|
||||
return;
|
||||
}
|
||||
}
|
||||
var entityMinPoint = props.boundingBox.brn;
|
||||
var entityMaxPoint = props.boundingBox.tfl;
|
||||
var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint);
|
||||
var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint);
|
||||
|
||||
if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) {
|
||||
// we haven't been touched before, but either right or left is touching us now
|
||||
_this.allTouchedIDs[id] = true;
|
||||
_this.startTouch(id);
|
||||
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) {
|
||||
// we have been touched before and are still being touched
|
||||
// continue touch
|
||||
_this.continueTouch(id);
|
||||
} else if (_this.allTouchedIDs[id]) {
|
||||
delete _this.allTouchedIDs[id];
|
||||
_this.stopTouch(id);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.startTouch = function(entityID) {
|
||||
this.callEntityMethodOnGrabbed("startTouch");
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "startTouch", args);
|
||||
};
|
||||
|
||||
this.continueTouch = function(entityID) {
|
||||
this.callEntityMethodOnGrabbed("continueTouch");
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "continueTouch", args);
|
||||
};
|
||||
|
||||
this.stopTouch = function(entityID) {
|
||||
this.callEntityMethodOnGrabbed("stopTouch");
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(entityID, "stopTouch", args);
|
||||
};
|
||||
|
||||
this.release = function() {
|
||||
|
@ -1886,7 +1887,7 @@ function cleanup() {
|
|||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
Controller.setReticleVisible(true);
|
||||
Reticle.setVisible(true);
|
||||
}
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -24,9 +24,9 @@ var PITCH_SCALING = 10.0;
|
|||
var YAW_SCALING = 10.0;
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
var lastPos = Reticle.getPosition();
|
||||
function moveReticle(dY, dX) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
var globalPos = Reticle.getPosition();
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
|
@ -45,7 +45,7 @@ function moveReticle(dY, dX) {
|
|||
|
||||
globalPos.x += dX;
|
||||
globalPos.y += dY;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
Reticle.setPosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ function length(posA, posB) {
|
|||
}
|
||||
|
||||
var EXPECTED_CHANGE = 50;
|
||||
var lastPos = Controller.getReticlePosition();
|
||||
var lastPos = Reticle.getPosition();
|
||||
function moveReticle(dX, dY) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
var globalPos = Reticle.getPosition();
|
||||
|
||||
// some debugging to see if position is jumping around on us...
|
||||
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||
|
@ -52,7 +52,7 @@ function moveReticle(dX, dY) {
|
|||
|
||||
globalPos.x += dX;
|
||||
globalPos.y += dY;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
Reticle.setPosition(globalPos);
|
||||
lastPos = globalPos;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ function length(posA, posB) {
|
|||
}
|
||||
|
||||
function moveReticleAbsolute(x, y) {
|
||||
var globalPos = Controller.getReticlePosition();
|
||||
var globalPos = Reticle.getPosition();
|
||||
globalPos.x = x;
|
||||
globalPos.y = y;
|
||||
Controller.setReticlePosition(globalPos);
|
||||
Reticle.setPosition(globalPos);
|
||||
}
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
||||
|
@ -53,8 +53,9 @@ Script.update.connect(function(deltaTime) {
|
|||
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
|
||||
|
||||
// NOTE: hack for now
|
||||
var screenSizeX = 1920;
|
||||
var screenSizeY = 1080;
|
||||
var screenSize = Reticle.maximumPosition;
|
||||
var screenSizeX = screenSize.x;
|
||||
var screenSizeY = screenSize.y;
|
||||
|
||||
var rotatedRight = Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y);
|
||||
var rotatedLeft = Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y);
|
||||
|
|
|
@ -38,6 +38,10 @@ var lightOverlayManager = new LightOverlayManager();
|
|||
var cameraManager = new CameraManager();
|
||||
|
||||
var grid = Grid();
|
||||
gridTool = GridTool({
|
||||
horizontalGrid: grid
|
||||
});
|
||||
gridTool.setVisible(false);
|
||||
|
||||
var entityListTool = EntityListTool();
|
||||
|
||||
|
@ -341,7 +345,7 @@ var toolBar = (function() {
|
|||
isActive = active;
|
||||
if (!isActive) {
|
||||
entityListTool.setVisible(false);
|
||||
// gridTool.setVisible(false);
|
||||
gridTool.setVisible(false);
|
||||
grid.setEnabled(false);
|
||||
propertiesTool.setVisible(false);
|
||||
selectionManager.clearSelections();
|
||||
|
@ -349,7 +353,7 @@ var toolBar = (function() {
|
|||
} else {
|
||||
hasShownPropertiesTool = false;
|
||||
entityListTool.setVisible(true);
|
||||
// gridTool.setVisible(true);
|
||||
gridTool.setVisible(true);
|
||||
grid.setEnabled(true);
|
||||
propertiesTool.setVisible(true);
|
||||
Window.setFocus();
|
||||
|
|
|
@ -158,10 +158,7 @@ Mouse.prototype.startRotateDrag = function() {
|
|||
x: this.current.x,
|
||||
y: this.current.y
|
||||
};
|
||||
this.cursorRestore = {
|
||||
x: Window.getCursorPositionX(),
|
||||
y: Window.getCursorPositionY()
|
||||
};
|
||||
this.cursorRestore = Reticle.getPosition();
|
||||
}
|
||||
|
||||
Mouse.prototype.getDrag = function() {
|
||||
|
@ -177,7 +174,7 @@ Mouse.prototype.getDrag = function() {
|
|||
}
|
||||
|
||||
Mouse.prototype.restoreRotateCursor = function() {
|
||||
Window.setCursorPosition(this.cursorRestore.x, this.cursorRestore.y);
|
||||
Reticle.setPosition(this.cursorRestore);
|
||||
this.current = {
|
||||
x: this.rotateStart.x,
|
||||
y: this.rotateStart.y
|
||||
|
|
|
@ -54,8 +54,8 @@ var velocity = { x: 0, y: 0, z: 0 };
|
|||
var velocityVertical = 0;
|
||||
var enabled = false;
|
||||
|
||||
var lastX = Window.getCursorPositionX();
|
||||
var lastY = Window.getCursorPositionY();
|
||||
var lastX = Reticle.getPosition().x;
|
||||
var lastY = Reticle.getPosition().y;
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
|
@ -109,8 +109,8 @@ function update(dt) {
|
|||
}
|
||||
|
||||
if (enabled && Window.hasFocus()) {
|
||||
var x = Window.getCursorPositionX();
|
||||
var y = Window.getCursorPositionY();
|
||||
var x = Reticle.getPosition().x;
|
||||
var y = Reticle.getPosition().y;
|
||||
|
||||
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
|
@ -173,7 +173,7 @@ function scriptEnding() {
|
|||
function resetCursorPosition() {
|
||||
var newX = Window.x + Window.innerWidth / 2;
|
||||
var newY = Window.y + Window.innerHeight / 2;
|
||||
Window.setCursorPosition(newX, newY);
|
||||
Reticle.setPosition({ x: newX, y: newY});
|
||||
lastX = newX;
|
||||
lastY = newY;
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ function enable() {
|
|||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||
}
|
||||
Window.setCursorVisible(false);
|
||||
Reticle.setVisible(false);
|
||||
Script.update.connect(update);
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ function disable() {
|
|||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||
}
|
||||
Window.setCursorVisible(true);
|
||||
Reticle.setVisible(true);
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,17 @@
|
|||
);
|
||||
};
|
||||
}
|
||||
|
||||
function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
|
||||
var newString = "";
|
||||
if (checkboxElement.checked) {
|
||||
newString += name + "";
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
|
||||
return function () {
|
||||
var properties = {};
|
||||
|
@ -207,6 +218,28 @@
|
|||
}
|
||||
};
|
||||
|
||||
function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
|
||||
if (subPropertyElement.checked) {
|
||||
if (propertyValue.indexOf(subPropertyString)) {
|
||||
propertyValue += subPropertyString + ',';
|
||||
}
|
||||
} else {
|
||||
// We've unchecked, so remove
|
||||
propertyValue = propertyValue.replace(subPropertyString + ",", "");
|
||||
}
|
||||
|
||||
var _properties ={}
|
||||
_properties[propertyName] = propertyValue;
|
||||
|
||||
EventBridge.emitWebEvent(
|
||||
JSON.stringify({
|
||||
type: "update",
|
||||
properties: _properties
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function loaded() {
|
||||
openEventBridge(function() {
|
||||
var allSections = [];
|
||||
|
@ -263,6 +296,11 @@
|
|||
var elDensity = document.getElementById("property-density");
|
||||
var elCollisionless = document.getElementById("property-collisionless");
|
||||
var elDynamic = document.getElementById("property-dynamic" );
|
||||
var elCollideStatic = document.getElementById("property-collide-static");
|
||||
var elCollideDynamic = document.getElementById("property-collide-dynamic");
|
||||
var elCollideKinematic = document.getElementById("property-collide-kinematic");
|
||||
var elCollideMyAvatar = document.getElementById("property-collide-myAvatar");
|
||||
var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar");
|
||||
var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
|
||||
var elLifetime = document.getElementById("property-lifetime");
|
||||
var elScriptURL = document.getElementById("property-script-url");
|
||||
|
@ -313,15 +351,7 @@
|
|||
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
|
||||
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
|
||||
var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
|
||||
|
||||
var elParticleSections = document.querySelectorAll(".particle-section");
|
||||
allSections.push(elParticleSections);
|
||||
var elParticleIsEmitting = document.getElementById("property-particle-is-emitting");
|
||||
var elParticleMaxParticles = document.getElementById("property-particle-maxparticles");
|
||||
var elParticleLifeSpan = document.getElementById("property-particle-lifespan");
|
||||
var elParticleEmitRate = document.getElementById("property-particle-emit-rate");
|
||||
var elParticleRadius = document.getElementById("property-particle-radius");
|
||||
var elParticleTextures = document.getElementById("property-particle-textures");
|
||||
|
||||
|
||||
var elTextSections = document.querySelectorAll(".text-section");
|
||||
allSections.push(elTextSections);
|
||||
|
@ -379,6 +409,7 @@
|
|||
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
var properties;
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.type == "update") {
|
||||
|
@ -420,7 +451,7 @@
|
|||
var selected = false;
|
||||
}
|
||||
|
||||
var properties = data.selections[0].properties;
|
||||
properties = data.selections[0].properties;
|
||||
|
||||
elID.innerHTML = properties.id;
|
||||
|
||||
|
@ -434,6 +465,7 @@
|
|||
} else {
|
||||
enableChildren(document.getElementById("properties-list"), 'input');
|
||||
}
|
||||
|
||||
|
||||
elName.value = properties.name;
|
||||
|
||||
|
@ -482,6 +514,15 @@
|
|||
elDensity.value = properties.density.toFixed(4);
|
||||
elCollisionless.checked = properties.collisionless;
|
||||
elDynamic.checked = properties.dynamic;
|
||||
|
||||
|
||||
elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1;
|
||||
elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1;
|
||||
elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1;
|
||||
elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1;
|
||||
elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1;
|
||||
|
||||
|
||||
elCollisionSoundURL.value = properties.collisionSoundURL;
|
||||
elLifetime.value = properties.lifetime;
|
||||
elScriptURL.value = properties.script;
|
||||
|
@ -604,18 +645,6 @@
|
|||
elZoneSkyboxURL.value = properties.skybox.url;
|
||||
|
||||
showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
|
||||
} else if (properties.type == "ParticleEffect") {
|
||||
for (var i = 0; i < elParticleSections.length; i++) {
|
||||
elParticleSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
elParticleIsEmitting.checked = properties.isEmitting;
|
||||
elParticleMaxParticles.value = properties.maxParticles;
|
||||
elParticleLifeSpan.value = properties.lifespan.toFixed(2);
|
||||
elParticleEmitRate.value = properties.emitRate.toFixed(1);
|
||||
elParticleRadius.value = properties.particleRadius.toFixed(3);
|
||||
elParticleTextures.value = properties.textures;
|
||||
|
||||
} else if (properties.type == "PolyVox") {
|
||||
for (var i = 0; i < elPolyVoxSections.length; i++) {
|
||||
elPolyVoxSections[i].style.display = 'block';
|
||||
|
@ -704,6 +733,29 @@
|
|||
elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density'));
|
||||
elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless'));
|
||||
elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic'));
|
||||
|
||||
|
||||
|
||||
|
||||
elCollideDynamic.addEventListener('change', function() {
|
||||
updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic');
|
||||
});
|
||||
|
||||
elCollideKinematic.addEventListener('change', function() {
|
||||
updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic');
|
||||
});
|
||||
|
||||
elCollideStatic.addEventListener('change', function() {
|
||||
updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static');
|
||||
});
|
||||
elCollideMyAvatar.addEventListener('change', function() {
|
||||
updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar');
|
||||
});
|
||||
elCollideOtherAvatar.addEventListener('change', function() {
|
||||
updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar');
|
||||
});
|
||||
|
||||
|
||||
elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
|
||||
|
||||
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
|
||||
|
@ -752,13 +804,6 @@
|
|||
|
||||
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
|
||||
|
||||
elParticleIsEmitting.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isEmitting'));
|
||||
elParticleMaxParticles.addEventListener('change', createEmitNumberPropertyUpdateFunction('maxParticles'));
|
||||
elParticleLifeSpan.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifespan'));
|
||||
elParticleEmitRate.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitRate'));
|
||||
elParticleRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('particleRadius'));
|
||||
elParticleTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
|
||||
|
||||
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
||||
elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
|
||||
elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
|
||||
|
@ -1195,9 +1240,9 @@
|
|||
<div class="property">
|
||||
<div class="label">Position</div>
|
||||
<div class="value">
|
||||
<div class="input-area ">X<input class="coord" type="number" id="property-pos-x" step="0.1"><div class="prop-x"></div></div>
|
||||
<div class="input-area ">Y<input class="coord" type="number" id="property-pos-y" step="0.1"><div class="prop-y"></div></div>
|
||||
<div class="input-area ">Z<input class="coord" type="number" id="property-pos-z" step="0.1"><div class="prop-z"></div></div>
|
||||
<div class="input-area ">X<input class="coord" type='number' id="property-pos-x"><div class="prop-x"></div></div>
|
||||
<div class="input-area ">Y<input class="coord" type='number' id="property-pos-y"><div class="prop-y"></div></div>
|
||||
<div class="input-area ">Z<input class="coord" type='number' id="property-pos-z" ><div class="prop-z"></div></div>
|
||||
<div>
|
||||
<input type="button" id="move-selection-to-grid" value="Selection to Grid">
|
||||
<input type="button" id="move-all-to-grid" value="All to Grid">
|
||||
|
@ -1387,6 +1432,48 @@
|
|||
<span class="value">
|
||||
<input type='checkbox' id="property-dynamic">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class = "sub-section-header"> Collides With: </div>
|
||||
<div class = "sub-props-checkbox-group">
|
||||
<div class="property">
|
||||
<span class="label"> static</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-collide-static">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label"> dynamic</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-collide-dynamic">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label"> kinematic</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-collide-kinematic">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label"> myAvatar</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-collide-myAvatar">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label"> otherAvatar</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-collide-otherAvatar">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
elPosY.value = origin.y.toFixed(2);
|
||||
}
|
||||
|
||||
if (data.minorGridWidth !== undefined) {
|
||||
elMinorSpacing.value = data.minorGridWidth;
|
||||
if (data.minorGridEvery !== undefined) {
|
||||
elMinorSpacing.value = data.minorGridEvery;
|
||||
}
|
||||
|
||||
if (data.majorGridEvery !== undefined) {
|
||||
|
@ -60,7 +60,7 @@
|
|||
origin: {
|
||||
y: elPosY.value,
|
||||
},
|
||||
minorGridWidth: elMinorSpacing.value,
|
||||
minorGridEvery: elMinorSpacing.value,
|
||||
majorGridEvery: elMajorSpacing.value,
|
||||
gridColor: gridColor,
|
||||
colorIndex: gridColorIndex,
|
||||
|
@ -132,21 +132,21 @@
|
|||
<div id="horizontal-position" class="property-section">
|
||||
<label>Position (Y Axis)</label>
|
||||
<span>
|
||||
<input type='number' id="horiz-y" class="number" value="0.0" step="0.1"></input>
|
||||
<input type='number' id="horiz-y" class="number" step="0.1"></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Minor Grid Size</label>
|
||||
<span>
|
||||
<input type='number' id="minor-spacing" min="0" step="0.01", ></input>
|
||||
<input type='number' id="minor-spacing" min="0.2" step="0.2"></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Major Grid Every</label>
|
||||
<span>
|
||||
<input type='number' id="major-spacing" min="2" step="1", ></input>
|
||||
<input type='number' id="major-spacing" min="1" step="1", ></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -263,6 +263,10 @@ table#properties-list {
|
|||
border-bottom: 0.75pt solid #e5e5e5;
|
||||
}
|
||||
|
||||
.sub-props-checkbox-group {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#properties-list .label {
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -281,8 +281,8 @@ CameraManager = function() {
|
|||
|
||||
that.mouseMoveEvent = function(event) {
|
||||
if (that.enabled && that.mode != MODE_INACTIVE) {
|
||||
var x = Window.getCursorPositionX();
|
||||
var y = Window.getCursorPositionY();
|
||||
var x = Reticle.getPosition().x;
|
||||
var y = Reticle.getPosition().y;
|
||||
if (!hasDragged) {
|
||||
that.lastMousePosition.x = x;
|
||||
that.lastMousePosition.y = y;
|
||||
|
@ -337,7 +337,7 @@ CameraManager = function() {
|
|||
}
|
||||
|
||||
if (updatePosition) {
|
||||
Window.setCursorPosition(newX, newY);
|
||||
Reticle.setPosition({ x: newX, y: newY});
|
||||
}
|
||||
|
||||
that.lastMousePosition.x = newX;
|
||||
|
@ -384,7 +384,7 @@ CameraManager = function() {
|
|||
if (!that.enabled) return;
|
||||
|
||||
that.mode = MODE_INACTIVE;
|
||||
Window.setCursorVisible(true);
|
||||
Reticle.setVisible(true);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -12,24 +12,26 @@ Grid = function(opts) {
|
|||
];
|
||||
var colorIndex = 0;
|
||||
var gridAlpha = 0.6;
|
||||
var origin = { x: 0, y: 0, z: 0 };
|
||||
var origin = { x: 0, y: +MyAvatar.getJointPosition('LeftToeBase').y.toFixed(1) + 0.1, z: 0 };
|
||||
var scale = 500;
|
||||
var minorGridEvery = 1.0;
|
||||
var majorGridEvery = 5;
|
||||
var minorGridWidth = 0.2;
|
||||
var halfSize = 40;
|
||||
var yOffset = 0.001;
|
||||
|
||||
var worldSize = 16384;
|
||||
|
||||
var snapToGrid = false;
|
||||
|
||||
var gridOverlay = Overlays.addOverlay("grid", {
|
||||
position: { x: 0 , y: 0, z: 0 },
|
||||
visible: true,
|
||||
rotation: Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
dimensions: { x: scale, y: scale, z: scale },
|
||||
position: origin,
|
||||
visible: false,
|
||||
drawInFront: false,
|
||||
color: colors[0],
|
||||
alpha: gridAlpha,
|
||||
rotation: Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
minorGridWidth: 0.1,
|
||||
majorGridEvery: 2,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
});
|
||||
|
||||
that.visible = false;
|
||||
|
@ -39,17 +41,13 @@ Grid = function(opts) {
|
|||
return origin;
|
||||
}
|
||||
|
||||
that.getMinorIncrement = function() { return minorGridWidth; };
|
||||
that.getMajorIncrement = function() { return minorGridWidth * majorGridEvery; };
|
||||
|
||||
that.getMinorGridWidth = function() { return minorGridWidth; };
|
||||
that.setMinorGridWidth = function(value) {
|
||||
minorGridWidth = value;
|
||||
that.getMinorIncrement = function() { return minorGridEvery; };
|
||||
that.setMinorIncrement = function(value) {
|
||||
minorGridEvery = value;
|
||||
updateGrid();
|
||||
};
|
||||
|
||||
that.getMajorGridEvery = function() { return majorGridEvery; };
|
||||
that.setMajorGridEvery = function(value) {
|
||||
}
|
||||
that.getMajorIncrement = function() { return majorGridEvery; };
|
||||
that.setMajorIncrement = function(value) {
|
||||
majorGridEvery = value;
|
||||
updateGrid();
|
||||
};
|
||||
|
@ -106,7 +104,7 @@ Grid = function(opts) {
|
|||
dimensions = { x: 0, y: 0, z: 0 };
|
||||
}
|
||||
|
||||
var spacing = majorOnly ? (minorGridWidth * majorGridEvery) : minorGridWidth;
|
||||
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
|
||||
|
||||
position = Vec3.subtract(position, origin);
|
||||
|
||||
|
@ -122,7 +120,7 @@ Grid = function(opts) {
|
|||
return delta;
|
||||
}
|
||||
|
||||
var spacing = majorOnly ? (minorGridWidth * majorGridEvery) : minorGridWidth;
|
||||
var spacing = majorOnly ? majorGridEvery : minorGridEvery;
|
||||
|
||||
var snappedDelta = {
|
||||
x: Math.round(delta.x / spacing) * spacing,
|
||||
|
@ -135,9 +133,7 @@ Grid = function(opts) {
|
|||
|
||||
|
||||
that.setPosition = function(newPosition, noUpdate) {
|
||||
origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 });
|
||||
origin.x = 0;
|
||||
origin.z = 0;
|
||||
origin = { x: 0, y: newPosition.y, z: 0 };
|
||||
updateGrid();
|
||||
|
||||
if (!noUpdate) {
|
||||
|
@ -149,7 +145,7 @@ Grid = function(opts) {
|
|||
if (that.onUpdate) {
|
||||
that.onUpdate({
|
||||
origin: origin,
|
||||
minorGridWidth: minorGridWidth,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
gridSize: halfSize,
|
||||
visible: that.visible,
|
||||
|
@ -171,8 +167,8 @@ Grid = function(opts) {
|
|||
that.setPosition(pos, true);
|
||||
}
|
||||
|
||||
if (data.minorGridWidth) {
|
||||
minorGridWidth = data.minorGridWidth;
|
||||
if (data.minorGridEvery) {
|
||||
minorGridEvery = data.minorGridEvery;
|
||||
}
|
||||
|
||||
if (data.majorGridEvery) {
|
||||
|
@ -191,20 +187,22 @@ Grid = function(opts) {
|
|||
that.setVisible(data.visible, true);
|
||||
}
|
||||
|
||||
updateGrid();
|
||||
updateGrid(true);
|
||||
}
|
||||
|
||||
function updateGrid() {
|
||||
function updateGrid(noUpdate) {
|
||||
Overlays.editOverlay(gridOverlay, {
|
||||
position: { x: origin.y, y: origin.y, z: -origin.y },
|
||||
position: { x: 0, y: origin.y, z: 0 },
|
||||
visible: that.visible && that.enabled,
|
||||
minorGridWidth: minorGridWidth,
|
||||
minorGridEvery: minorGridEvery,
|
||||
majorGridEvery: majorGridEvery,
|
||||
color: colors[colorIndex],
|
||||
alpha: gridAlpha,
|
||||
});
|
||||
|
||||
that.emitUpdate();
|
||||
if (!noUpdate) {
|
||||
that.emitUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
|
@ -247,7 +245,7 @@ GridTool = function(opts) {
|
|||
} else if (data.type == "update") {
|
||||
horizontalGrid.update(data);
|
||||
for (var i = 0; i < listeners.length; i++) {
|
||||
listeners[i](data);
|
||||
listeners[i] && listeners[i](data);
|
||||
}
|
||||
} else if (data.type == "action") {
|
||||
var action = data.action;
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var lastX = Window.getCursorPositionX();
|
||||
var lastY = Window.getCursorPositionY();
|
||||
var lastX = Reticle.getPosition().x;
|
||||
var lastY = Reticle.getPosition().y;
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
|
@ -121,9 +121,9 @@ var mouseLook = (function () {
|
|||
|
||||
function onScriptUpdate(dt) {
|
||||
if (active && Window.hasFocus()) {
|
||||
var x = Window.getCursorPositionX();
|
||||
var x = Reticle.getPosition().x;
|
||||
// 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);
|
||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
|
@ -155,7 +155,7 @@ var mouseLook = (function () {
|
|||
function resetCursorPosition() {
|
||||
var newX = Window.x + Window.innerWidth / 2.0;
|
||||
var newY = Window.y + Window.innerHeight / 2.0;
|
||||
Window.setCursorPosition(newX, newY);
|
||||
Reticle.setPosition({ x: newX, y: newY});
|
||||
lastX = newX;
|
||||
lastY = newY;
|
||||
}
|
||||
|
|
|
@ -14,9 +14,6 @@ Render.RenderShadowTask.enabled = true;
|
|||
var RDT = Render.RenderDeferredTask;
|
||||
RDT.AmbientOcclusion.enabled = true;
|
||||
RDT.DebugDeferredBuffer.enabled = false;
|
||||
["DrawOpaqueDeferred", "DrawTransparentDeferred", "DrawOverlay3DOpaque", "DrawOverlay3DTransparent"]
|
||||
.map(function(name) { return RDT[name]; })
|
||||
.forEach(function(job) { job.maxDrawn = job.numDrawn; });
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('main.qml');
|
||||
|
@ -39,4 +36,4 @@ function setDebugBufferSize(x) {
|
|||
Render.RenderDeferredTask.DebugDeferredBuffer.size = {x: x, y: -1, z: 1, w: 1};
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() { Render.fromJSON(oldConfig); } );
|
||||
Script.scriptEnding.connect(function() { Render.load(oldConfig); } );
|
||||
|
|
|
@ -12,98 +12,109 @@ import QtQuick 2.5
|
|||
import QtQuick.Controls 1.4
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred",
|
||||
"Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: Render.getConfig(modelData.split(":")[1])
|
||||
property: "maxDrawn"
|
||||
max: config.numDrawn
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
CheckBox {
|
||||
text: qsTr("Display Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showDisplay = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Network/Physics Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showNetwork = checked }
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Tone Mapping Exposure")
|
||||
config: Render.getConfig("ToneMapping")
|
||||
property: "exposure"
|
||||
min: -10; max: 10
|
||||
id: root
|
||||
spacing: 16
|
||||
Switch {
|
||||
checked: true
|
||||
onClicked: ui.visible = checked
|
||||
}
|
||||
|
||||
Column {
|
||||
id: ambientOcclusion
|
||||
property var config: Render.getConfig("AmbientOcclusion")
|
||||
id: ui
|
||||
spacing: 8
|
||||
|
||||
Label { text: qsTr("Ambient Occlusion") }
|
||||
// TODO: Add gpuTimer
|
||||
CheckBox { text: qsTr("Dithering"); checked: ambientOcclusion.config.ditheringEnabled }
|
||||
Repeater {
|
||||
model: [
|
||||
"Resolution Level:resolutionLevel:4",
|
||||
"Obscurance Level:obscuranceLevel:1",
|
||||
"Radius:radius:2",
|
||||
"Falloff Bias:falloffBias:0.2",
|
||||
"Edge Sharpness:edgeSharpness:1",
|
||||
"Blur Radius:blurRadius:6",
|
||||
"Blur Deviation:blurDeviation:3"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: [
|
||||
"Samples:numSamples:32",
|
||||
"Spiral Turns:numSpiralTurns:30:"
|
||||
]
|
||||
model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred",
|
||||
"Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
config: Render.getConfig(modelData.split(":")[1])
|
||||
property: "maxDrawn"
|
||||
max: config.numDrawn
|
||||
min: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: debug
|
||||
property var config: Render.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
debug.config.enabled = (mode != 0);
|
||||
debug.config.mode = mode;
|
||||
Row {
|
||||
CheckBox {
|
||||
text: qsTr("Display Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showDisplay = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Network/Physics Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showNetwork = checked }
|
||||
}
|
||||
}
|
||||
|
||||
Label { text: qsTr("Debug Buffer") }
|
||||
ExclusiveGroup { id: bufferGroup }
|
||||
Repeater {
|
||||
model: [
|
||||
"Off", "Diffuse", "Metallic", "Roughness", "Normal", "Depth",
|
||||
"Lighting", "Shadow", "Pyramid Depth", "Ambient Occlusion", "Custom Shader"
|
||||
]
|
||||
RadioButton {
|
||||
text: qsTr(modelData)
|
||||
exclusiveGroup: bufferGroup
|
||||
checked: index == 0
|
||||
onCheckedChanged: if (checked) debug.setDebugMode(index);
|
||||
ConfigSlider {
|
||||
label: qsTr("Tone Mapping Exposure")
|
||||
config: Render.getConfig("ToneMapping")
|
||||
property: "exposure"
|
||||
min: -10; max: 10
|
||||
}
|
||||
|
||||
Column {
|
||||
id: ambientOcclusion
|
||||
property var config: Render.getConfig("AmbientOcclusion")
|
||||
|
||||
Label { text: qsTr("Ambient Occlusion") }
|
||||
// TODO: Add gpuTimer
|
||||
CheckBox { text: qsTr("Dithering"); checked: ambientOcclusion.config.ditheringEnabled }
|
||||
Repeater {
|
||||
model: [
|
||||
"Resolution Level:resolutionLevel:4",
|
||||
"Obscurance Level:obscuranceLevel:1",
|
||||
"Radius:radius:2",
|
||||
"Falloff Bias:falloffBias:0.2",
|
||||
"Edge Sharpness:edgeSharpness:1",
|
||||
"Blur Radius:blurRadius:6",
|
||||
"Blur Deviation:blurDeviation:3"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: [
|
||||
"Samples:numSamples:32",
|
||||
"Spiral Turns:numSpiralTurns:30:"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: debug
|
||||
property var config: Render.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
debug.config.enabled = (mode != 0);
|
||||
debug.config.mode = mode;
|
||||
}
|
||||
|
||||
Label { text: qsTr("Debug Buffer") }
|
||||
ExclusiveGroup { id: bufferGroup }
|
||||
Repeater {
|
||||
model: [
|
||||
"Off", "Diffuse", "Metallic", "Roughness", "Normal", "Depth",
|
||||
"Lighting", "Shadow", "Pyramid Depth", "Ambient Occlusion", "Custom Shader"
|
||||
]
|
||||
RadioButton {
|
||||
text: qsTr(modelData)
|
||||
exclusiveGroup: bufferGroup
|
||||
checked: index == 0
|
||||
onCheckedChanged: if (checked && index > 0) debug.setDebugMode(index - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ Window {
|
|||
destroyOnInvisible: true
|
||||
width: 800
|
||||
height: 600
|
||||
|
||||
property alias webView: webview
|
||||
|
||||
Component.onCompleted: {
|
||||
visible = true
|
||||
addressBar.text = webview.url
|
||||
|
@ -28,6 +29,7 @@ Window {
|
|||
}
|
||||
|
||||
Item {
|
||||
id:item
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
|
@ -125,12 +127,10 @@ Window {
|
|||
console.log("New icon: " + icon)
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
storageName: "qmlUserBrowser"
|
||||
}
|
||||
|
||||
profile: desktop.browserProfile
|
||||
|
||||
}
|
||||
|
||||
} // item
|
||||
|
||||
Keys.onPressed: {
|
||||
|
|
12
interface/resources/qml/ForceLoad.qml
Normal file
12
interface/resources/qml/ForceLoad.qml
Normal file
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.0
|
||||
import QtMultimedia 5.5
|
||||
|
||||
Item {
|
||||
Audio {
|
||||
id: audio
|
||||
autoLoad: true
|
||||
autoPlay: true
|
||||
loops: Audio.Infinite
|
||||
}
|
||||
}
|
||||
|
|
@ -267,15 +267,9 @@ Item {
|
|||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.showAcuity
|
||||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded && !root.showAcuity
|
||||
text: root.lodStatsRenderText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ WebEngineView {
|
|||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
||||
|
@ -36,6 +37,10 @@ WebEngineView {
|
|||
}
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
|
@ -48,9 +53,12 @@ WebEngineView {
|
|||
}
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
storageName: "qmlWebEngine"
|
||||
onNewViewRequested:{
|
||||
var component = Qt.createComponent("../Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView)
|
||||
}
|
||||
|
||||
|
||||
profile: desktop.browserProfile
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ Desktop {
|
|||
id: desktop
|
||||
|
||||
Component.onCompleted: {
|
||||
WebEngine.settings.javascriptCanOpenWindows = false;
|
||||
WebEngine.settings.javascriptCanOpenWindows = true;
|
||||
WebEngine.settings.javascriptCanAccessClipboard = false;
|
||||
WebEngine.settings.spatialNavigationEnabled = true;
|
||||
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||
|
@ -19,6 +19,12 @@ Desktop {
|
|||
property alias toolWindow: toolWindow
|
||||
ToolWindow { id: toolWindow }
|
||||
|
||||
property var browserProfile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Chrome/48.0 (HighFidelityInterface)"
|
||||
storageName: "qmlWebEngine"
|
||||
}
|
||||
|
||||
Action {
|
||||
text: "Open Browser"
|
||||
shortcut: "Ctrl+B"
|
||||
|
|
|
@ -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 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 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)) {
|
||||
auto globalPos = QCursor::pos();
|
||||
auto localPos = _glWidget->mapFromGlobal(globalPos);
|
||||
auto reticlePos = _compositor.getReticlePosition();
|
||||
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) {
|
||||
QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
||||
sendEvent(_glWidget, &mousePress);
|
||||
|
@ -806,39 +809,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||
cycleCamera();
|
||||
} 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)) {
|
||||
auto oldPos = QCursor::pos();
|
||||
auto newPos = oldPos;
|
||||
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;
|
||||
}
|
||||
|
||||
auto oldPos = _compositor.getReticlePosition();
|
||||
_compositor.setReticlePosition({ oldPos.x + state, oldPos.y });
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
||||
auto oldPos = QCursor::pos();
|
||||
auto newPos = oldPos;
|
||||
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;
|
||||
}
|
||||
auto oldPos = _compositor.getReticlePosition();
|
||||
_compositor.setReticlePosition({ oldPos.x, oldPos.y + state });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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
|
||||
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,
|
||||
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) {
|
||||
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
if (_keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
|
@ -1279,13 +1257,15 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
||||
rootContext->setContextProperty("Reticle", _compositor.getReticleInterface());
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
QPointF result = pt;
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
if (displayPlugin->isHmd()) {
|
||||
auto resultVec = _compositor.screenToOverlay(toGlm(pt));
|
||||
_compositor.handleRealMouseMoveEvent(false);
|
||||
auto resultVec = _compositor.getReticlePosition();
|
||||
result = QPointF(resultVec.x, resultVec.y);
|
||||
}
|
||||
return result.toPoint();
|
||||
|
@ -1532,7 +1512,7 @@ void Application::paintGL() {
|
|||
// just relying on the left FOV in each case and hoping that the
|
||||
// overall culling margin of error doesn't cause popping in the
|
||||
// right eye. There are FIXMEs in the relevant plugins
|
||||
_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection()));
|
||||
_myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection()));
|
||||
renderArgs._context->enableStereo(true);
|
||||
mat4 eyeOffsets[2];
|
||||
mat4 eyeProjections[2];
|
||||
|
@ -1563,7 +1543,7 @@ void Application::paintGL() {
|
|||
|
||||
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
|
||||
|
||||
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
|
||||
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
|
||||
});
|
||||
renderArgs._context->setStereoProjections(eyeProjections);
|
||||
renderArgs._context->setStereoViews(eyeOffsets);
|
||||
|
@ -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;
|
||||
_lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance);
|
||||
_pendingPaint = false;
|
||||
}
|
||||
|
||||
|
@ -1733,6 +1707,7 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
if ((int)event->type() == (int)Paint) {
|
||||
paintGL();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_keyboardFocusedItem.isInvalidID()) {
|
||||
|
@ -1824,6 +1799,11 @@ bool Application::event(QEvent* event) {
|
|||
}
|
||||
|
||||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||
|
||||
if (event->type() == QEvent::Leave) {
|
||||
_compositor.handleLeaveEvent();
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
|
||||
event->accept();
|
||||
|
@ -2079,9 +2059,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
case Qt::Key_Space: {
|
||||
if (!event->isAutoRepeat()) {
|
||||
// FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious
|
||||
// this starts an HFActionEvent
|
||||
HFActionEvent startActionEvent(HFActionEvent::startType(),
|
||||
computePickRay(getTrueMouse().x, getTrueMouse().y));
|
||||
computePickRay(getMouse().x, getMouse().y));
|
||||
sendEvent(this, &startActionEvent);
|
||||
}
|
||||
|
||||
|
@ -2110,7 +2091,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
||||
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());
|
||||
|
@ -2129,9 +2111,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
switch (event->key()) {
|
||||
case Qt::Key_Space: {
|
||||
if (!event->isAutoRepeat()) {
|
||||
// FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious
|
||||
// this ends the HFActionEvent
|
||||
HFActionEvent endActionEvent(HFActionEvent::endType(),
|
||||
computePickRay(getTrueMouse().x, getTrueMouse().y));
|
||||
computePickRay(getMouse().x, getMouse().y));
|
||||
sendEvent(this, &endActionEvent);
|
||||
}
|
||||
break;
|
||||
|
@ -2175,13 +2158,7 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
_keysPressed.clear();
|
||||
}
|
||||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
if (_aboutToQuit) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Application::maybeToggleMenuVisible(QMouseEvent* event) {
|
||||
#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
|
||||
// then show the menubar.
|
||||
|
@ -2193,7 +2170,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
if (event->pos().y() <= MENU_TOGGLE_AREA) {
|
||||
menuBar->setVisible(true);
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
if (event->pos().y() > MENU_TOGGLE_AREA) {
|
||||
menuBar->setVisible(false);
|
||||
}
|
||||
|
@ -2201,9 +2178,36 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
}
|
||||
#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>();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
auto button = event->button();
|
||||
auto buttons = event->buttons();
|
||||
// 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,
|
||||
buttons, event->modifiers());
|
||||
|
||||
getEntities()->mouseMoveEvent(&mappedEvent, deviceID);
|
||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
|
||||
getEntities()->mouseMoveEvent(&mappedEvent);
|
||||
_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 (_controllerScriptingInterface->isMouseCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
_keyboardMouseDevice->mouseMoveEvent(event, deviceID);
|
||||
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
_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
|
||||
_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
|
||||
// will consume all keyboard events.
|
||||
offscreenUi->unfocusWindows();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
|
||||
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
getEntities()->mousePressEvent(&mappedEvent, deviceID);
|
||||
getEntities()->mousePressEvent(&mappedEvent);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
@ -2262,7 +2268,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
|
||||
if (hasFocus()) {
|
||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
_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 (_controllerScriptingInterface->isMouseCaptured()) {
|
||||
return;
|
||||
|
@ -2285,17 +2291,18 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI
|
|||
_controllerScriptingInterface->emitMouseDoublePressEvent(event);
|
||||
}
|
||||
|
||||
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
void Application::mouseReleaseEvent(QMouseEvent* event) {
|
||||
|
||||
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(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
getEntities()->mouseReleaseEvent(&mappedEvent, deviceID);
|
||||
getEntities()->mouseReleaseEvent(&mappedEvent);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
@ -2306,7 +2313,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
if (hasFocus()) {
|
||||
if (deviceID == 0 && Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) {
|
||||
_keyboardMouseDevice->mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
|
@ -2566,11 +2573,16 @@ void Application::setLowVelocityFilter(bool 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()) {
|
||||
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() {
|
||||
|
@ -3082,11 +3094,7 @@ void Application::update(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
if (DependencyManager::get<LODManager>()->getUseAcuity()) {
|
||||
updateLOD();
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->updatePIDRenderDistance(getTargetFrameRate(), getLastInstanteousFps(), deltaTime, isThrottleRendering());
|
||||
}
|
||||
updateLOD();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
|
@ -3898,16 +3906,8 @@ void Application::resetSensors(bool andReload) {
|
|||
DependencyManager::get<Faceshift>()->reset();
|
||||
DependencyManager::get<DdeFaceTracker>()->reset();
|
||||
DependencyManager::get<EyeTracker>()->reset();
|
||||
|
||||
getActiveDisplayPlugin()->resetSensors();
|
||||
|
||||
QScreen* currentScreen = _window->windowHandle()->screen();
|
||||
QWindow* mainWindow = _window->windowHandle();
|
||||
QPoint windowCenter = mainWindow->geometry().center();
|
||||
_glWidget->cursor().setPos(currentScreen, windowCenter);
|
||||
|
||||
getMyAvatar()->reset(andReload);
|
||||
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
@ -4240,6 +4240,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface());
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) const {
|
||||
|
@ -4711,6 +4712,14 @@ glm::uvec2 Application::getCanvasSize() const {
|
|||
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 {
|
||||
return getActiveDisplayPlugin()->getRecommendedUiSize();
|
||||
}
|
||||
|
@ -4719,18 +4728,10 @@ QSize Application::getDeviceSize() const {
|
|||
return fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
||||
}
|
||||
|
||||
PickRay Application::computePickRay() const {
|
||||
return computePickRay(getTrueMouse().x, getTrueMouse().y);
|
||||
}
|
||||
|
||||
bool Application::isThrottleRendering() const {
|
||||
return getActiveDisplayPlugin()->isThrottled();
|
||||
}
|
||||
|
||||
ivec2 Application::getTrueMouse() const {
|
||||
return toGlm(_glWidget->mapFromGlobal(QCursor::pos()));
|
||||
}
|
||||
|
||||
bool Application::hasFocus() const {
|
||||
return getActiveDisplayPlugin()->hasFocus();
|
||||
}
|
||||
|
@ -5005,7 +5006,7 @@ void Application::updateInputModes() {
|
|||
|
||||
mat4 Application::getEyeProjection(int eye) const {
|
||||
if (isHMDMode()) {
|
||||
return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection());
|
||||
return getActiveDisplayPlugin()->getEyeProjection((Eye)eye, _viewFrustum.getProjection());
|
||||
}
|
||||
|
||||
return _viewFrustum.getProjection();
|
||||
|
|
|
@ -114,10 +114,11 @@ public:
|
|||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
glm::uvec2 getCanvasSize() const;
|
||||
QRect getRenderingGeometry() const;
|
||||
|
||||
glm::uvec2 getUiSize() const;
|
||||
QSize getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
PickRay computePickRay() const;
|
||||
|
||||
bool isThrottleRendering() const;
|
||||
|
||||
|
@ -139,8 +140,7 @@ public:
|
|||
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
||||
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
||||
|
||||
ivec2 getMouse() const;
|
||||
ivec2 getTrueMouse() const;
|
||||
ivec2 getMouse();
|
||||
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
FaceTracker* getSelectedFaceTracker();
|
||||
|
@ -157,7 +157,6 @@ public:
|
|||
float getFps() const { return _fps; }
|
||||
float getTargetFrameRate(); // frames/second
|
||||
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
||||
float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; }
|
||||
|
||||
float getFieldOfView() { return _fieldOfView.get(); }
|
||||
void setFieldOfView(float fov);
|
||||
|
@ -219,6 +218,8 @@ public:
|
|||
|
||||
float getAverageSimsPerSecond();
|
||||
|
||||
void fakeMouseEvent(QMouseEvent* event);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
|
@ -368,10 +369,10 @@ private:
|
|||
void focusOutEvent(QFocusEvent* event);
|
||||
void focusInEvent(QFocusEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoublePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
void touchBeginEvent(QTouchEvent* event);
|
||||
void touchEndEvent(QTouchEvent* event);
|
||||
|
@ -381,6 +382,7 @@ private:
|
|||
void dropEvent(QDropEvent* event);
|
||||
void dragEnterEvent(QDragEnterEvent* event);
|
||||
|
||||
void maybeToggleMenuVisible(QMouseEvent* event);
|
||||
|
||||
bool _dependencyManagerIsSetup;
|
||||
|
||||
|
@ -402,7 +404,6 @@ private:
|
|||
QElapsedTimer _timerStart;
|
||||
QElapsedTimer _lastTimeUpdated;
|
||||
float _lastInstantaneousFps { 0.0f };
|
||||
float _lastUnsynchronizedFps { 0.0f };
|
||||
|
||||
ShapeManager _shapeManager;
|
||||
PhysicalEntitySimulation _entitySimulation;
|
||||
|
@ -510,6 +511,8 @@ private:
|
|||
bool _settingsLoaded { false };
|
||||
bool _pendingPaint { false };
|
||||
QTimer* _idleTimer { nullptr };
|
||||
|
||||
bool _fakedMouseEvent { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -20,29 +20,8 @@
|
|||
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_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() {
|
||||
|
||||
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() {
|
||||
|
@ -234,53 +213,7 @@ QString LODManager::getLODFeedbackText() {
|
|||
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) {
|
||||
// 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
|
||||
// to use in rendering.
|
||||
float renderAccuracy = args->_viewFrustum->calculateRenderAccuracy(bounds, args->_sizeScale, args->_boundaryLevelAdjust);
|
||||
|
@ -299,12 +232,6 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
|||
void LODManager::loadSettings() {
|
||||
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.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() {
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include <PIDController.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0;
|
||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 30.0;
|
||||
const float DEFAULT_DESKTOP_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_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
|
||||
const float INCREASE_LOD_GAP = 15.0f;
|
||||
|
@ -76,27 +76,6 @@ public:
|
|||
Q_INVOKABLE float getLODDecreaseFPS();
|
||||
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);
|
||||
void autoAdjustLOD(float currentFPS);
|
||||
|
||||
|
@ -126,9 +105,6 @@ private:
|
|||
SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES;
|
||||
SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES;
|
||||
SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES;
|
||||
|
||||
PIDController _renderDistanceController{};
|
||||
SimpleMovingAverage _renderDistanceAverage{ 10 };
|
||||
};
|
||||
|
||||
#endif // hifi_LODManager_h
|
||||
|
|
|
@ -968,6 +968,7 @@ int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPositi
|
|||
return requestedPosition;
|
||||
}
|
||||
|
||||
bool Menu::_isSomeSubmenuShown = false;
|
||||
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
|
@ -994,6 +995,12 @@ MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ public:
|
|||
QAction* newAction() {
|
||||
return new QAction(_realMenu);
|
||||
}
|
||||
|
||||
private:
|
||||
MenuWrapper(QMenu* menu);
|
||||
|
||||
|
@ -117,6 +118,8 @@ public slots:
|
|||
void toggleDeveloperMenus();
|
||||
void toggleAdvancedMenus();
|
||||
|
||||
static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; }
|
||||
|
||||
private:
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
|
@ -142,6 +145,8 @@ private:
|
|||
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
||||
QHash<QString, bool> _groupingVisible;
|
||||
QHash<QString, QSet<QAction*>> _groupingActions;
|
||||
|
||||
static bool _isSomeSubmenuShown;
|
||||
};
|
||||
|
||||
namespace MenuOption {
|
||||
|
|
|
@ -117,8 +117,8 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
|
|||
static const glm::vec4 inputColor = { 0.3f, 1.0f, 0.3f, 1.0f };
|
||||
static const glm::vec4 outputLeftColor = { 1.0f, 0.3f, 0.3f, 1.0f };
|
||||
static const glm::vec4 outputRightColor = { 0.3f, 0.3f, 1.0f, 1.0f };
|
||||
static const int gridRows = 2;
|
||||
int gridCols = _framesPerScope;
|
||||
static const int gridCols = 2;
|
||||
int gridRows = _framesPerScope;
|
||||
|
||||
int x = (width - (int)SCOPE_WIDTH) / 2;
|
||||
int y = (height - (int)SCOPE_HEIGHT) / 2;
|
||||
|
@ -127,6 +127,12 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
|
|||
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// Grid uses its own pipeline, so draw it before setting another
|
||||
const float GRID_EDGE = 0.005f;
|
||||
geometryCache->renderGrid(batch, glm::vec2(x, y), glm::vec2(x + w, y + h),
|
||||
gridRows, gridCols, GRID_EDGE, gridColor, true, _audioScopeGrid);
|
||||
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
batch.setResourceTexture(0, textureCache->getWhiteTexture());
|
||||
|
@ -139,7 +145,6 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
|
|||
batch.setViewTransform(Transform());
|
||||
|
||||
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
|
||||
geometryCache->renderGrid(batch, x, y, w, h, gridRows, gridCols, gridColor, _audioScopeGrid);
|
||||
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);
|
||||
renderLineStrip(batch, _outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft);
|
||||
renderLineStrip(batch, _outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight);
|
||||
|
|
|
@ -162,7 +162,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
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;
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
|
|
|
@ -421,8 +421,8 @@ private:
|
|||
AtRestDetector _hmdAtRestDetector;
|
||||
bool _lastIsMoving { false };
|
||||
bool _hoverReferenceCameraFacingIsCaptured { false };
|
||||
glm::vec3 _hoverReferenceCameraFacing; // hmd sensor space
|
||||
|
||||
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
|
||||
|
||||
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
||||
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
||||
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
|
||||
|
|
|
@ -144,6 +144,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
} else {
|
||||
handParams.isRightEnabled = false;
|
||||
}
|
||||
handParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
|
||||
|
||||
_rig->updateFromHandParameters(handParams, deltaTime);
|
||||
|
||||
|
|
|
@ -164,10 +164,10 @@ InputController::Key InputController::getKey() const {
|
|||
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(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::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
|
||||
void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
|
||||
void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
|
||||
void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); }
|
||||
void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); }
|
||||
void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event) { emit mouseDoublePressEvent(MouseEvent(*event)); }
|
||||
void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); }
|
||||
|
||||
void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
|
||||
void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
|
||||
|
|
|
@ -70,10 +70,10 @@ public:
|
|||
|
||||
void handleMetaEvent(HFMetaEvent* event);
|
||||
|
||||
void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void emitMouseMoveEvent(QMouseEvent* event);
|
||||
void emitMousePressEvent(QMouseEvent* event);
|
||||
void emitMouseDoublePressEvent(QMouseEvent* event);
|
||||
void emitMouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
void emitTouchBeginEvent(const TouchEvent& event);
|
||||
void emitTouchEndEvent(const TouchEvent& event);
|
||||
|
@ -111,10 +111,10 @@ signals:
|
|||
void backStartEvent();
|
||||
void backEndEvent();
|
||||
|
||||
void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
||||
void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
||||
void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
||||
void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0);
|
||||
void mouseMoveEvent(const MouseEvent& event);
|
||||
void mousePressEvent(const MouseEvent& event);
|
||||
void mouseDoublePressEvent(const MouseEvent& event);
|
||||
void mouseReleaseEvent(const MouseEvent& event);
|
||||
|
||||
void touchBeginEvent(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);
|
||||
}
|
||||
|
||||
glm::vec3 HMDScriptingInterface::worldPointFromOverlay(const glm::vec2& overlay) const {
|
||||
return qApp->getApplicationCompositor().sphereSurfaceFromOverlay(overlay);
|
||||
}
|
||||
|
||||
glm::vec2 HMDScriptingInterface::sphericalToOverlay(const glm::vec2 & position) const {
|
||||
return qApp->getApplicationCompositor().sphericalToOverlay(position);
|
||||
}
|
||||
|
@ -38,16 +42,6 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & 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) {
|
||||
glm::vec3 hudIntersection;
|
||||
auto instance = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
|
|
@ -29,11 +29,10 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
|||
public:
|
||||
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::vec3 worldPointFromOverlay(const glm::vec2& overlay) 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 screenToOverlay(const glm::vec2 & screenPos) const;
|
||||
Q_INVOKABLE glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
|
||||
|
||||
public:
|
||||
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
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
|
|
|
@ -32,9 +32,6 @@ public:
|
|||
int getY();
|
||||
|
||||
public slots:
|
||||
QScriptValue getCursorPositionX();
|
||||
QScriptValue getCursorPositionY();
|
||||
void setCursorPosition(int x, int y);
|
||||
QScriptValue hasFocus();
|
||||
void setFocus();
|
||||
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 CURSOR_PIXEL_SIZE = 32.0f;
|
||||
static const float MOUSE_PITCH_RANGE = 1.0f * PI;
|
||||
static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
|
||||
static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE);
|
||||
|
||||
static gpu::BufferPointer _hemiVertices;
|
||||
static gpu::BufferPointer _hemiIndices;
|
||||
|
@ -112,7 +109,9 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r,
|
|||
}
|
||||
|
||||
ApplicationCompositor::ApplicationCompositor() :
|
||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha"))
|
||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")),
|
||||
_reticleInterface(new ReticleInterface(this))
|
||||
|
||||
{
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
|
@ -214,7 +213,7 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
|
||||
//draw the mouse pointer
|
||||
// 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);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
|
@ -244,7 +243,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
|
||||
updateTooltips();
|
||||
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
glm::uvec2 screenSize = qApp->getUiSize(); // HMD use virtual screen size
|
||||
vec2 canvasSize = screenSize;
|
||||
_textureAspectRatio = aspect(canvasSize);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -257,9 +257,9 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
mat4 camMat;
|
||||
_cameraBaseTransform.getMatrix(camMat);
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
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.setViewTransform(camMat);
|
||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||
|
@ -282,15 +282,13 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
bindCursorTexture(batch);
|
||||
|
||||
//Mouse Pointer
|
||||
auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
|
||||
bool reticleVisible = controllerScriptingInterface->getReticleVisible();
|
||||
if (reticleVisible) {
|
||||
if (getReticleVisible()) {
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
|
||||
glm::vec2 projection = screenToSpherical(qApp->getTrueMouse());
|
||||
|
||||
float cursorDepth = controllerScriptingInterface->getReticleDepth();
|
||||
auto reticlePosition = getReticlePosition();
|
||||
glm::vec2 projection = overlayToSpherical(reticlePosition);
|
||||
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 reticleXfm = overlayXfm * pointerXfm;
|
||||
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
|
||||
// we should kill it asap.
|
||||
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
||||
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));
|
||||
bool ApplicationCompositor::shouldCaptureMouse() const {
|
||||
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
|
||||
return qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown();
|
||||
}
|
||||
|
||||
// The orientation and position of the HEAD, not the overlay
|
||||
glm::vec3 worldSpaceHeadPosition = qApp->getCamera()->getPosition();
|
||||
glm::quat worldSpaceOrientation = qApp->getCamera()->getOrientation();
|
||||
void ApplicationCompositor::handleLeaveEvent() {
|
||||
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto headOrientation = glm::quat_cast(headPose);
|
||||
auto headTranslation = extractTranslation(headPose);
|
||||
if (shouldCaptureMouse()) {
|
||||
QWidget* mainWidget = (QWidget*)qApp->getWindow();
|
||||
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);
|
||||
auto overlayPosition = worldSpaceHeadPosition - (overlayOrientation * headTranslation);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StandingHMDSensorMode)) {
|
||||
overlayPosition = _modelTransform.getTranslation();
|
||||
overlayOrientation = _modelTransform.getRotation();
|
||||
if (intersection.left() > uncoveredRect.left()) {
|
||||
uncoveredRect.setRight(intersection.left() - 1);
|
||||
} else if (intersection.right() < uncoveredRect.right()) {
|
||||
uncoveredRect.setLeft(intersection.right() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_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
|
||||
glm::vec3 worldSpaceIntersection = ((overlayOrientation * (cursorOrientation * Vectors::FRONT)) * _oculusUIRadius) + overlayPosition;
|
||||
// If we're in HMD mode
|
||||
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;
|
||||
direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);
|
||||
glm::vec2 ApplicationCompositor::getReticlePosition() {
|
||||
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
|
||||
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
||||
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||
|
@ -459,25 +562,6 @@ void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
|
|||
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 result = sphericalPos;
|
||||
result.x *= -1.0f;
|
||||
|
@ -498,18 +582,8 @@ glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos
|
|||
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 {
|
||||
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
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 center = vec3(0); // center of HUD 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;
|
||||
auto overlayPos = sphericalToOverlay(polar);
|
||||
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() {
|
||||
if (_hoverItemId != _noItemId) {
|
||||
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||
|
|
|
@ -9,9 +9,13 @@
|
|||
#ifndef hifi_ApplicationCompositor_h
|
||||
#define hifi_ApplicationCompositor_h
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QCursor>
|
||||
#include <QMouseEvent>
|
||||
#include <QObject>
|
||||
#include <QPropertyAnimation>
|
||||
#include <cstdint>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <GeometryCache.h>
|
||||
|
@ -22,12 +26,18 @@
|
|||
class Camera;
|
||||
class PalmData;
|
||||
class RenderArgs;
|
||||
class ReticleInterface;
|
||||
|
||||
|
||||
|
||||
const float MAGNIFY_WIDTH = 220.0f;
|
||||
const float MAGNIFY_HEIGHT = 100.0f;
|
||||
const float MAGNIFY_MULT = 2.0f;
|
||||
|
||||
const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
|
||||
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
|
||||
// TODO, move divide up the rendering, displaying and input handling
|
||||
|
@ -50,19 +60,15 @@ public:
|
|||
|
||||
// Converter from one frame of reference to another.
|
||||
// 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)
|
||||
// Overlay: Position on the overlay (x,y)
|
||||
// (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen.
|
||||
// This allows for picking outside of the screen projection in 3D.
|
||||
glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||
glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||
glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
|
||||
glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
|
||||
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||
uint32_t getOverlayTexture() const;
|
||||
|
||||
glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
|
||||
glm::vec3 sphereSurfaceFromOverlay(const glm::vec2& overlay) const;
|
||||
|
||||
void setCameraBaseTransform(const Transform& transform) { _cameraBaseTransform = transform; }
|
||||
const Transform& getCameraBaseTransform() const { return _cameraBaseTransform; }
|
||||
|
@ -77,10 +83,28 @@ public:
|
|||
float getAlpha() const { return _alpha; }
|
||||
void setAlpha(float alpha) { _alpha = alpha; }
|
||||
|
||||
static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
|
||||
static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
|
||||
bool getReticleVisible() { return _reticleVisible; }
|
||||
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);
|
||||
|
||||
bool shouldCaptureMouse() const;
|
||||
|
||||
private:
|
||||
|
||||
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
|
||||
|
@ -94,8 +118,8 @@ private:
|
|||
QString _hoverItemDescription;
|
||||
quint64 _hoverItemEnterUsecs { 0 };
|
||||
|
||||
float _hmdUIAngularSize { DEFAULT_HMD_UI_ANGULAR_SIZE };
|
||||
float _textureFov { glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) };
|
||||
float _hmdUIAngularSize { DEFAULT_HMD_UI_VERT_ANGULAR_SIZE };
|
||||
float _textureFov { glm::radians(DEFAULT_HMD_UI_VERT_ANGULAR_SIZE) };
|
||||
float _textureAspectRatio { 1.0f };
|
||||
int _hemiVerticesID { GeometryCache::UNKNOWN_ID };
|
||||
|
||||
|
@ -115,6 +139,50 @@ private:
|
|||
Transform _cameraBaseTransform;
|
||||
|
||||
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)
|
||||
Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
|
||||
|
||||
public:
|
||||
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
|
||||
|
||||
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
|
||||
|
||||
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
|
||||
|
|
|
@ -251,12 +251,9 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
|
|||
void ApplicationOverlay::buildFramebufferObject() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
QSize desiredSize = qApp->getDeviceSize();
|
||||
int currentWidth = _overlayFramebuffer ? _overlayFramebuffer->getWidth() : 0;
|
||||
int currentHeight = _overlayFramebuffer ? _overlayFramebuffer->getHeight() : 0;
|
||||
QSize frameBufferCurrentSize(currentWidth, currentHeight);
|
||||
|
||||
if (_overlayFramebuffer && desiredSize == frameBufferCurrentSize) {
|
||||
auto uiSize = qApp->getUiSize();
|
||||
|
||||
if (_overlayFramebuffer && uiSize == _overlayFramebuffer->getSize()) {
|
||||
// Already built
|
||||
return;
|
||||
}
|
||||
|
@ -270,8 +267,8 @@ void ApplicationOverlay::buildFramebufferObject() {
|
|||
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
|
||||
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
auto width = desiredSize.width();
|
||||
auto height = desiredSize.height();
|
||||
auto width = uiSize.x;
|
||||
auto height = uiSize.y;
|
||||
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
_overlayColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <gpu/Texture.h>
|
||||
|
||||
|
||||
// Handles the drawing of the overlays to the screen
|
||||
// TODO, move divide up the rendering, displaying and input handling
|
||||
// facilities of this class
|
||||
|
|
|
@ -87,13 +87,6 @@ void setupPreferences() {
|
|||
}
|
||||
|
||||
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 setter = [](float value) { DependencyManager::get<LODManager>()->setDesktopLODDecreaseFPS(value); };
|
||||
|
@ -101,7 +94,6 @@ void setupPreferences() {
|
|||
preference->setMin(0);
|
||||
preference->setMax(120);
|
||||
preference->setStep(1);
|
||||
preference->setEnabler(acuityToggle);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
|
@ -112,18 +104,6 @@ void setupPreferences() {
|
|||
preference->setMin(0);
|
||||
preference->setMax(120);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -274,7 +254,7 @@ void setupPreferences() {
|
|||
{
|
||||
auto getter = []()->float { return DependencyManager::get<AudioClient>()->getOutputBufferSize(); };
|
||||
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->setMax(20);
|
||||
preference->setStep(1);
|
||||
|
|
|
@ -283,9 +283,7 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
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 displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||
|
|
|
@ -30,7 +30,6 @@ class Stats : public QQuickItem {
|
|||
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
||||
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
|
||||
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
||||
Q_PROPERTY(bool showAcuity READ getShowAcuity WRITE setShowAcuity NOTIFY showAcuityChanged)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, renderrate, 0)
|
||||
|
@ -80,7 +79,6 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(QString, packetStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatsRenderText, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
STATS_PROPERTY(int, serverInternal, 0)
|
||||
STATS_PROPERTY(int, serverLeaves, 0)
|
||||
|
@ -112,15 +110,12 @@ public:
|
|||
emit expandedChanged();
|
||||
}
|
||||
}
|
||||
bool getShowAcuity() { return _showAcuity; }
|
||||
void setShowAcuity(bool newValue) { _showAcuity = newValue; }
|
||||
|
||||
public slots:
|
||||
void forceUpdateStats() { updateStats(true); }
|
||||
|
||||
signals:
|
||||
void expandedChanged();
|
||||
void showAcuityChanged();
|
||||
void timingExpandedChanged();
|
||||
void serverCountChanged();
|
||||
void renderrateChanged();
|
||||
|
@ -128,7 +123,6 @@ signals:
|
|||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void avatarCountChanged();
|
||||
void lodStatsRenderTextChanged();
|
||||
void packetInCountChanged();
|
||||
void packetOutCountChanged();
|
||||
void mbpsInChanged();
|
||||
|
@ -182,7 +176,6 @@ private:
|
|||
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
||||
bool _resetRecentMaxPacketsSoon{ true };
|
||||
bool _expanded{ false };
|
||||
bool _showAcuity{ false };
|
||||
bool _timingExpanded{ false };
|
||||
QString _monospaceFont;
|
||||
const AudioIOStats* _audioStats;
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <QScriptValue>
|
||||
|
||||
#include <OctreeConstants.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <PathUtils.h>
|
||||
|
@ -20,17 +22,28 @@
|
|||
|
||||
|
||||
QString const Grid3DOverlay::TYPE = "grid";
|
||||
const float DEFAULT_SCALE = 100.0f;
|
||||
|
||||
Grid3DOverlay::Grid3DOverlay() :
|
||||
_minorGridWidth(1.0),
|
||||
_majorGridEvery(5) {
|
||||
Grid3DOverlay::Grid3DOverlay() {
|
||||
setDimensions(DEFAULT_SCALE);
|
||||
updateGrid();
|
||||
}
|
||||
|
||||
Grid3DOverlay::Grid3DOverlay(const Grid3DOverlay* grid3DOverlay) :
|
||||
Planar3DOverlay(grid3DOverlay),
|
||||
_minorGridWidth(grid3DOverlay->_minorGridWidth),
|
||||
_majorGridEvery(grid3DOverlay->_majorGridEvery)
|
||||
_majorGridEvery(grid3DOverlay->_majorGridEvery),
|
||||
_minorGridEvery(grid3DOverlay->_minorGridEvery)
|
||||
{
|
||||
updateGrid();
|
||||
}
|
||||
|
||||
AABox Grid3DOverlay::getBounds() const {
|
||||
if (_followCamera) {
|
||||
// This is a UI element that should always be in view, lie to the octree to avoid culling
|
||||
const AABox DOMAIN_BOX = AABox(glm::vec3(-TREE_SCALE / 2), TREE_SCALE);
|
||||
return DOMAIN_BOX;
|
||||
}
|
||||
return Planar3DOverlay::getBounds();
|
||||
}
|
||||
|
||||
void Grid3DOverlay::render(RenderArgs* args) {
|
||||
|
@ -38,15 +51,8 @@ void Grid3DOverlay::render(RenderArgs* args) {
|
|||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
const int MINOR_GRID_DIVISIONS = 200;
|
||||
const int MAJOR_GRID_DIVISIONS = 100;
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
||||
// center the grid around the camera position on the plane
|
||||
glm::vec3 rotated = glm::inverse(getRotation()) * args->_viewFrustum->getPosition();
|
||||
|
||||
float spacing = _minorGridWidth;
|
||||
|
||||
float alpha = getAlpha();
|
||||
xColor color = getColor();
|
||||
glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
@ -54,72 +60,65 @@ void Grid3DOverlay::render(RenderArgs* args) {
|
|||
auto batch = args->_batch;
|
||||
|
||||
if (batch) {
|
||||
auto minCorner = glm::vec2(-0.5f, -0.5f);
|
||||
auto maxCorner = glm::vec2(0.5f, 0.5f);
|
||||
|
||||
auto position = getPosition();
|
||||
if (_followCamera) {
|
||||
// Get the camera position rounded to the nearest major grid line
|
||||
// This grid is for UI and should lie on worldlines
|
||||
auto cameraPosition =
|
||||
(float)_majorGridEvery * glm::round(args->_viewFrustum->getPosition() / (float)_majorGridEvery);
|
||||
|
||||
position += glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z);
|
||||
}
|
||||
|
||||
Transform transform;
|
||||
transform.setRotation(getRotation());
|
||||
transform.setScale(glm::vec3(getDimensions(), 1.0f));
|
||||
transform.setTranslation(position);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
|
||||
// Minor grid
|
||||
{
|
||||
auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2),
|
||||
getPosition().z);
|
||||
float scale = MINOR_GRID_DIVISIONS * spacing;
|
||||
|
||||
transform.setTranslation(position);
|
||||
transform.setScale(scale);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
|
||||
// Major grid
|
||||
{
|
||||
spacing *= _majorGridEvery;
|
||||
auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2),
|
||||
getPosition().z);
|
||||
float scale = MAJOR_GRID_DIVISIONS * spacing;
|
||||
|
||||
transform.setTranslation(position);
|
||||
transform.setScale(scale);
|
||||
|
||||
// FIXME: THe line width of 4.0f is not supported anymore, we ll need a workaround
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);
|
||||
}
|
||||
const float MINOR_GRID_EDGE = 0.0025f;
|
||||
const float MAJOR_GRID_EDGE = 0.005f;
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
|
||||
_minorGridRowDivisions, _minorGridColDivisions, MINOR_GRID_EDGE,
|
||||
_majorGridRowDivisions, _majorGridColDivisions, MAJOR_GRID_EDGE,
|
||||
gridColor, _drawInFront);
|
||||
}
|
||||
}
|
||||
|
||||
const render::ShapeKey Grid3DOverlay::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (getAlpha() != 1.0f) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
return builder.build();
|
||||
return render::ShapeKey::Builder().withOwnPipeline();
|
||||
}
|
||||
|
||||
void Grid3DOverlay::setProperties(const QScriptValue& properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
|
||||
if (properties.property("minorGridWidth").isValid()) {
|
||||
_minorGridWidth = properties.property("minorGridWidth").toVariant().toFloat();
|
||||
if (properties.property("followCamera").isValid()) {
|
||||
_followCamera = properties.property("followCamera").toVariant().toBool();
|
||||
}
|
||||
|
||||
if (properties.property("majorGridEvery").isValid()) {
|
||||
_majorGridEvery = properties.property("majorGridEvery").toVariant().toInt();
|
||||
}
|
||||
|
||||
if (properties.property("minorGridEvery").isValid()) {
|
||||
_minorGridEvery = properties.property("minorGridEvery").toVariant().toFloat();
|
||||
}
|
||||
|
||||
updateGrid();
|
||||
}
|
||||
|
||||
QScriptValue Grid3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "minorGridWidth") {
|
||||
return _minorGridWidth;
|
||||
if (property == "followCamera") {
|
||||
return _followCamera;
|
||||
}
|
||||
if (property == "majorGridEvery") {
|
||||
return _majorGridEvery;
|
||||
}
|
||||
if (property == "minorGridEvery") {
|
||||
return _minorGridEvery;
|
||||
}
|
||||
|
||||
return Planar3DOverlay::getProperty(property);
|
||||
}
|
||||
|
@ -128,3 +127,16 @@ Grid3DOverlay* Grid3DOverlay::createClone() const {
|
|||
return new Grid3DOverlay(this);
|
||||
}
|
||||
|
||||
void Grid3DOverlay::updateGrid() {
|
||||
const int MAJOR_GRID_EVERY_MIN = 1;
|
||||
const float MINOR_GRID_EVERY_MIN = 0.01f;
|
||||
|
||||
_majorGridEvery = std::max(_majorGridEvery, MAJOR_GRID_EVERY_MIN);
|
||||
_minorGridEvery = std::max(_minorGridEvery, MINOR_GRID_EVERY_MIN);
|
||||
|
||||
_majorGridRowDivisions = getDimensions().x / _majorGridEvery;
|
||||
_majorGridColDivisions = getDimensions().y / _majorGridEvery;
|
||||
|
||||
_minorGridRowDivisions = getDimensions().x / _minorGridEvery;
|
||||
_minorGridColDivisions = getDimensions().y / _minorGridEvery;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ public:
|
|||
Grid3DOverlay();
|
||||
Grid3DOverlay(const Grid3DOverlay* grid3DOverlay);
|
||||
|
||||
virtual AABox getBounds() const;
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual const render::ShapeKey getShapeKey() override;
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
|
@ -31,9 +33,21 @@ public:
|
|||
|
||||
virtual Grid3DOverlay* createClone() const;
|
||||
|
||||
// Grids are UI tools, and may not be intersected (pickable)
|
||||
virtual bool findRayIntersection(const glm::vec3&, const glm::vec3&, float&, BoxFace&, glm::vec3&) { return false; }
|
||||
|
||||
private:
|
||||
float _minorGridWidth;
|
||||
int _majorGridEvery;
|
||||
void updateGrid();
|
||||
|
||||
bool _followCamera { true };
|
||||
|
||||
int _majorGridEvery { 5 };
|
||||
float _majorGridRowDivisions;
|
||||
float _majorGridColDivisions;
|
||||
|
||||
float _minorGridEvery { 1.0f };
|
||||
float _minorGridRowDivisions;
|
||||
float _minorGridColDivisions;
|
||||
};
|
||||
|
||||
#endif // hifi_Grid3DOverlay_h
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
Planar3DOverlay();
|
||||
Planar3DOverlay(const Planar3DOverlay* planar3DOverlay);
|
||||
|
||||
AABox getBounds() const;
|
||||
virtual AABox getBounds() const;
|
||||
|
||||
glm::vec2 getDimensions() const { return _dimensions; }
|
||||
void setDimensions(float value) { _dimensions = glm::vec2(value); }
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <RenderDeferredTask.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
||||
const float DEFAULT_MARGIN = 0.1f;
|
||||
const int FIXED_FONT_POINT_SIZE = 40;
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
|
|
@ -670,7 +670,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
|
||||
|
||||
std::vector<float> minDots;
|
||||
const float MAX_SHOULDER_SWING = PI / 20.0f;
|
||||
const float MAX_SHOULDER_SWING = PI / 6.0f;
|
||||
minDots.push_back(cosf(MAX_SHOULDER_SWING));
|
||||
stConstraint->setSwingLimits(minDots);
|
||||
|
||||
|
|
|
@ -1080,8 +1080,31 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
|
||||
void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
||||
if (_animSkeleton && _animNode) {
|
||||
|
||||
const float HAND_RADIUS = 0.05f;
|
||||
const float BODY_RADIUS = params.bodyCapsuleRadius;
|
||||
const float MIN_LENGTH = 1.0e-4f;
|
||||
|
||||
// project the hips onto the xz plane.
|
||||
auto hipsTrans = _internalPoseSet._absolutePoses[_animSkeleton->nameToJointIndex("Hips")].trans;
|
||||
const glm::vec2 bodyCircleCenter(hipsTrans.x, hipsTrans.z);
|
||||
|
||||
if (params.isLeftEnabled) {
|
||||
_animVars.set("leftHandPosition", params.leftPosition);
|
||||
|
||||
// project the hand position onto the xz plane.
|
||||
glm::vec2 handCircleCenter(params.leftPosition.x, params.leftPosition.z);
|
||||
|
||||
// check for 2d overlap of the hand and body circles.
|
||||
auto circleToCircle = handCircleCenter - bodyCircleCenter;
|
||||
const float circleToCircleLength = glm::length(circleToCircle);
|
||||
const float penetrationDistance = HAND_RADIUS + BODY_RADIUS - circleToCircleLength;
|
||||
if (penetrationDistance > 0.0f && circleToCircleLength > MIN_LENGTH) {
|
||||
// push the hands out of the body
|
||||
handCircleCenter += penetrationDistance * glm::normalize(circleToCircle);
|
||||
}
|
||||
|
||||
glm::vec3 handPosition(handCircleCenter.x, params.leftPosition.y, handCircleCenter.y);
|
||||
_animVars.set("leftHandPosition", handPosition);
|
||||
_animVars.set("leftHandRotation", params.leftOrientation);
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
} else {
|
||||
|
@ -1089,8 +1112,23 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
|||
_animVars.unset("leftHandRotation");
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
}
|
||||
|
||||
if (params.isRightEnabled) {
|
||||
_animVars.set("rightHandPosition", params.rightPosition);
|
||||
|
||||
// project the hand position onto the xz plane.
|
||||
glm::vec2 handCircleCenter(params.rightPosition.x, params.rightPosition.z);
|
||||
|
||||
// check for 2d overlap of the hand and body circles.
|
||||
auto circleToCircle = handCircleCenter - bodyCircleCenter;
|
||||
const float circleToCircleLength = glm::length(circleToCircle);
|
||||
const float penetrationDistance = HAND_RADIUS + BODY_RADIUS - circleToCircleLength;
|
||||
if (penetrationDistance > 0.0f && circleToCircleLength > MIN_LENGTH) {
|
||||
// push the hands out of the body
|
||||
handCircleCenter += penetrationDistance * glm::normalize(circleToCircle);
|
||||
}
|
||||
|
||||
glm::vec3 handPosition(handCircleCenter.x, params.rightPosition.y, handCircleCenter.y);
|
||||
_animVars.set("rightHandPosition", handPosition);
|
||||
_animVars.set("rightHandRotation", params.rightOrientation);
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
} else {
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
struct HandParameters {
|
||||
bool isLeftEnabled;
|
||||
bool isRightEnabled;
|
||||
float bodyCapsuleRadius;
|
||||
glm::vec3 leftPosition = glm::vec3(); // rig space
|
||||
glm::quat leftOrientation = glm::quat(); // rig space (z forward)
|
||||
glm::vec3 rightPosition = glm::vec3(); // rig space
|
||||
|
|
|
@ -80,8 +80,9 @@ AudioClient::AudioClient() :
|
|||
_isStereoInput(false),
|
||||
_outputStarveDetectionStartTimeMsec(0),
|
||||
_outputStarveDetectionCount(0),
|
||||
_outputBufferSizeFrames("audioOutputBufferSize", DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES),
|
||||
_outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled",
|
||||
_outputBufferSizeFrames("audioOutputBufferSizeFrames", DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES),
|
||||
_sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()),
|
||||
_outputStarveDetectionEnabled("audioOutputBufferStarveDetectionEnabled",
|
||||
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED),
|
||||
_outputStarveDetectionPeriodMsec("audioOutputStarveDetectionPeriod",
|
||||
DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_PERIOD),
|
||||
|
@ -109,6 +110,7 @@ AudioClient::AudioClient() :
|
|||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||
connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { switchOutputToAudioDevice(outputDeviceInfo); });
|
||||
|
||||
_inputDevices = getDeviceNames(QAudio::AudioInput);
|
||||
_outputDevices = getDeviceNames(QAudio::AudioOutput);
|
||||
|
@ -277,6 +279,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||
const QAudioFormat& desiredAudioFormat,
|
||||
QAudioFormat& adjustedAudioFormat) {
|
||||
|
||||
// FIXME: directly using 24khz has a bug somewhere that causes channels to be swapped.
|
||||
// Continue using our internal resampler, for now.
|
||||
if (true || !audioDevice.isFormatSupported(desiredAudioFormat)) {
|
||||
|
@ -971,10 +974,8 @@ void AudioClient::outputNotify() {
|
|||
_outputStarveDetectionStartTimeMsec = now;
|
||||
_outputStarveDetectionCount = 0;
|
||||
|
||||
int oldOutputBufferSizeFrames = _outputBufferSizeFrames.get();
|
||||
int newOutputBufferSizeFrames = oldOutputBufferSizeFrames + 1;
|
||||
setOutputBufferSize(newOutputBufferSizeFrames);
|
||||
newOutputBufferSizeFrames = _outputBufferSizeFrames.get();
|
||||
int oldOutputBufferSizeFrames = _sessionOutputBufferSizeFrames;
|
||||
int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false);
|
||||
if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) {
|
||||
qCDebug(audioclient) << "Starve detection threshold met, increasing buffer size to " << newOutputBufferSizeFrames;
|
||||
}
|
||||
|
@ -1038,15 +1039,19 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
|
||||
// setup our general output device for audio-mixer audio
|
||||
_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);
|
||||
|
||||
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize;
|
||||
|
||||
_audioOutputIODevice.start();
|
||||
_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
|
||||
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
|
@ -1060,19 +1065,23 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
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);
|
||||
if (numFrames != _outputBufferSizeFrames.get()) {
|
||||
if (numFrames != _sessionOutputBufferSizeFrames) {
|
||||
qCDebug(audioclient) << "Audio output buffer size (frames): " << numFrames;
|
||||
_outputBufferSizeFrames.set(numFrames);
|
||||
_sessionOutputBufferSizeFrames = numFrames;
|
||||
if (persist) {
|
||||
_outputBufferSizeFrames.set(numFrames);
|
||||
}
|
||||
|
||||
if (_audioOutput) {
|
||||
// The buffer size can't be adjusted after QAudioOutput::start() has been called, so
|
||||
// recreate the device by switching to the default.
|
||||
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
|
||||
|
@ -1143,6 +1152,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
}
|
||||
|
||||
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
|
||||
if (!bytesAudioOutputUnplayed) {
|
||||
qCDebug(audioclient) << "empty audio buffer";
|
||||
}
|
||||
if (bytesAudioOutputUnplayed == 0 && bytesWritten == 0) {
|
||||
_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 MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1;
|
||||
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 = false;
|
||||
#else
|
||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
|
||||
#endif
|
||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
|
||||
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_THRESHOLD = 3;
|
||||
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 sendMuteEnvironmentPacket();
|
||||
|
||||
void setOutputBufferSize(int numFrames);
|
||||
int setOutputBufferSize(int numFrames, bool persist = true);
|
||||
|
||||
virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector);
|
||||
|
||||
|
@ -184,6 +180,7 @@ signals:
|
|||
void outputBytesToNetwork(int numBytes);
|
||||
void inputBytesFromNetwork(int numBytes);
|
||||
|
||||
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo);
|
||||
void deviceChanged();
|
||||
|
||||
void receivedFirstPacket();
|
||||
|
@ -230,6 +227,7 @@ private:
|
|||
int _outputStarveDetectionCount;
|
||||
|
||||
Setting::Handle<int> _outputBufferSizeFrames;
|
||||
int _sessionOutputBufferSizeFrames;
|
||||
Setting::Handle<bool> _outputStarveDetectionEnabled;
|
||||
Setting::Handle<int> _outputStarveDetectionPeriodMsec;
|
||||
// Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size
|
||||
|
|
|
@ -106,71 +106,6 @@ namespace controller {
|
|||
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() {
|
||||
return DependencyManager::get<UserInputMapper>()->getAllActions();
|
||||
}
|
||||
|
|
|
@ -90,46 +90,6 @@ namespace controller {
|
|||
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
||||
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& getActions() { return _actions; }
|
||||
Q_INVOKABLE const QVariantMap& getStandard() { return _standard; }
|
||||
|
@ -170,9 +130,6 @@ namespace controller {
|
|||
bool _touchCaptured{ false };
|
||||
bool _wheelCaptured{ false };
|
||||
bool _actionsCaptured{ false };
|
||||
|
||||
bool _reticleVisible{ true };
|
||||
float _reticleDepth { 1.0f };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/02/15
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "HmdDisplayPlugin.h"
|
||||
|
||||
#include <memory>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include "../Logging.h"
|
||||
|
||||
static const QString MONO_PREVIEW = "Mono Preview";
|
||||
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
||||
static const bool DEFAULT_MONO_VIEW = true;
|
||||
|
||||
void HmdDisplayPlugin::activate() {
|
||||
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
||||
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
|
||||
[this](bool clicked) {
|
||||
_monoPreview = clicked;
|
||||
_container->setBoolSetting("monoPreview", _monoPreview);
|
||||
}, true, _monoPreview);
|
||||
_container->removeMenu(FRAMERATE);
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::deactivate() {
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::customizeContext() {
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
enableVsync(false);
|
||||
_enablePreview = !isVsyncEnabled();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::internalPresent() {
|
||||
// screen preview mirroring
|
||||
if (_enablePreview) {
|
||||
auto windowSize = toGlm(_window->size());
|
||||
float windowAspect = aspect(windowSize);
|
||||
float sceneAspect = aspect(_renderTargetSize);
|
||||
if (_monoPreview) {
|
||||
sceneAspect /= 2.0f;
|
||||
}
|
||||
float aspectRatio = sceneAspect / windowAspect;
|
||||
|
||||
uvec2 targetViewportSize = windowSize;
|
||||
if (aspectRatio < 1.0f) {
|
||||
targetViewportSize.x *= aspectRatio;
|
||||
} else {
|
||||
targetViewportSize.y /= aspectRatio;
|
||||
}
|
||||
|
||||
uvec2 targetViewportPosition;
|
||||
if (targetViewportSize.x < windowSize.x) {
|
||||
targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2;
|
||||
} else if (targetViewportSize.y < windowSize.y) {
|
||||
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportSize.x * (_monoPreview ? 2 : 1), targetViewportSize.y);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportSize.x, targetViewportSize.y);
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
GLenum err = glGetError();
|
||||
Q_ASSERT(0 == err);
|
||||
drawUnitQuad();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
swapBuffers();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/02/15
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "../WindowOpenGLDisplayPlugin.h"
|
||||
|
||||
class HmdDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
public:
|
||||
bool isHmd() const override final { return true; }
|
||||
float getIPD() const override final { return _ipd; }
|
||||
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 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
|
||||
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; }
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
protected:
|
||||
void internalPresent() override;
|
||||
void customizeContext() override;
|
||||
|
||||
std::array<glm::mat4, 2> _eyeOffsets;
|
||||
std::array<glm::mat4, 2> _eyeProjections;
|
||||
glm::mat4 _cullingProjection;
|
||||
glm::uvec2 _renderTargetSize;
|
||||
float _ipd { 0.064f };
|
||||
private:
|
||||
bool _enablePreview { false };
|
||||
bool _monoPreview { true };
|
||||
};
|
||||
|
|
@ -39,16 +39,11 @@ const float DEFAULT_SEPARATION = DEFAULT_IPD / DEFAULT_SCREEN_WIDTH;
|
|||
// Default convergence depth: where is the screen plane in the virtual space (which depth)
|
||||
const float DEFAULT_CONVERGENCE = 0.5f;
|
||||
|
||||
glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
glm::mat4 StereoDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
// Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating
|
||||
// stereo projection matrices. Do NOT use "toe-in", use translation.
|
||||
// Updated version: http://developer.download.nvidia.com/assets/gamedev/docs/Siggraph2011-Stereoscopy_From_XY_to_Z-SG.pdf
|
||||
|
||||
if (eye == Mono) {
|
||||
// FIXME provide a combined matrix, needed for proper culling
|
||||
return baseProjection;
|
||||
}
|
||||
|
||||
float frustumshift = DEFAULT_SEPARATION;
|
||||
if (eye == Right) {
|
||||
frustumshift = -frustumshift;
|
||||
|
|
|
@ -20,8 +20,8 @@ public:
|
|||
virtual void deactivate() override;
|
||||
|
||||
virtual float getRecommendedAspectRatio() const override;
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
|
||||
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
|
||||
// NOTE, because Stereo displays don't include head tracking, and therefore
|
||||
// can't include roll or pitch, the eye separation is embedded into the projection
|
||||
// matrix. However, this eliminates the possibility of easily mainpulating
|
||||
|
|
|
@ -492,16 +492,16 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
|||
|
||||
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId){
|
||||
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event){
|
||||
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
||||
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
||||
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) {
|
||||
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event));
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
// process these events.
|
||||
if (!_tree || _shuttingDown) {
|
||||
|
@ -540,20 +540,20 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
|||
|
||||
}
|
||||
|
||||
emit mousePressOnEntity(rayPickResult, event, deviceID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event, deviceID));
|
||||
emit mousePressOnEntity(rayPickResult, event);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event));
|
||||
|
||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event, deviceID));
|
||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event));
|
||||
} else {
|
||||
emit mousePressOffEntity(rayPickResult, event, deviceID);
|
||||
emit mousePressOffEntity(rayPickResult, event);
|
||||
}
|
||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_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
|
||||
// process these events.
|
||||
if (!_tree || _shuttingDown) {
|
||||
|
@ -565,24 +565,24 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
|||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
emit mouseReleaseOnEntity(rayPickResult, event, deviceID);
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event, deviceID));
|
||||
emit mouseReleaseOnEntity(rayPickResult, event);
|
||||
_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
|
||||
// we're releasing the button, then this is considered a clickOn event
|
||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event, deviceID));
|
||||
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event));
|
||||
}
|
||||
|
||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_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
|
||||
// process these events.
|
||||
if (!_tree || _shuttingDown) {
|
||||
|
@ -596,28 +596,28 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
|||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||
if (rayPickResult.intersects) {
|
||||
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event));
|
||||
|
||||
// handle the hover logic...
|
||||
|
||||
// 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.
|
||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||
_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
|
||||
// this is true if the _currentHoverOverEntityID is known or unknown
|
||||
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
|
||||
// we should send our hover over event
|
||||
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event, deviceID));
|
||||
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event));
|
||||
|
||||
// remember what we're hovering over
|
||||
_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
|
||||
// send the hover leave for our previous entity
|
||||
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event, deviceID));
|
||||
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event));
|
||||
_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
|
||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event, deviceID));
|
||||
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event));
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event));
|
||||
}
|
||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||
_lastMouseEvent = MouseEvent(*event);
|
||||
_lastMouseEventValid = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,9 +74,9 @@ public:
|
|||
void deleteReleasedModels();
|
||||
|
||||
// event handles which may generate entity related events
|
||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID);
|
||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID);
|
||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
|
||||
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
|
||||
/// hovering over, and entering entities
|
||||
|
@ -86,10 +86,10 @@ public:
|
|||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseReleaseOnEntity(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);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
|
||||
void clickDownOnEntity(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 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 entity{ new RenderableWebEntityItem(entityID) };
|
||||
|
@ -41,28 +46,123 @@ RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemI
|
|||
}
|
||||
|
||||
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
||||
if (_webSurface) {
|
||||
_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);
|
||||
destroyWebSurface();
|
||||
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) {
|
||||
|
||||
#ifdef WANT_EXTRA_DEBUGGING
|
||||
{
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
@ -72,116 +172,19 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
#endif
|
||||
|
||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||
QSurface * currentSurface = currentContext->surface();
|
||||
if (!_webSurface) {
|
||||
_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;
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
if (!buildWebSurface(static_cast<EntityTreeRenderer*>(args->_renderer))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
glm::vec2 dims = glm::vec2(getDimensions());
|
||||
dims *= METERS_TO_INCHES * DPI;
|
||||
// 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
|
||||
// without worrying about excessive overhead.
|
||||
_webSurface->resize(QSize(dims.x, dims.y));
|
||||
currentContext->makeCurrent(currentSurface);
|
||||
|
||||
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Web);
|
||||
|
@ -223,3 +226,37 @@ void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) {
|
|||
QObject* RenderableWebEntityItem::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 QWindow;
|
||||
class QObject;
|
||||
class EntityTreeRenderer;
|
||||
|
||||
class RenderableWebEntityItem : public WebEntityItem {
|
||||
public:
|
||||
|
@ -31,15 +32,22 @@ public:
|
|||
void setProxyWindow(QWindow* proxyWindow);
|
||||
QObject* getEventHandler();
|
||||
|
||||
void update(const quint64& now) override;
|
||||
bool needsToCallUpdate() const { return _webSurface != nullptr; }
|
||||
|
||||
SIMPLE_RENDERABLE();
|
||||
|
||||
private:
|
||||
bool buildWebSurface(EntityTreeRenderer* renderer);
|
||||
void destroyWebSurface();
|
||||
|
||||
OffscreenQmlSurface* _webSurface{ nullptr };
|
||||
QMetaObject::Connection _connection;
|
||||
uint32_t _texture{ 0 };
|
||||
ivec2 _lastPress{ INT_MIN };
|
||||
bool _pressed{ false };
|
||||
ivec2 _lastMove{ INT_MIN };
|
||||
uint64_t _lastRenderTime{ 0 };
|
||||
|
||||
QMetaObject::Connection _mousePressConnection;
|
||||
QMetaObject::Connection _mouseReleaseConnection;
|
||||
|
|
|
@ -133,11 +133,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
float cost = calculateCost(density * volume, 0, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if(cost > _currentAvatarEnergy) {
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
return QUuid();
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
|
||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||
|
@ -173,6 +170,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
|
||||
// queue the packet
|
||||
if (success) {
|
||||
emit debitEnergySource(cost);
|
||||
queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
|
||||
}
|
||||
|
||||
|
@ -232,7 +230,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
|
||||
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
|
||||
EntityItemProperties properties = scriptSideProperties;
|
||||
|
||||
|
||||
auto dimensions = properties.getDimensions();
|
||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
auto density = properties.getDensity();
|
||||
|
@ -242,18 +240,18 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
EntityItemID entityID(id);
|
||||
if (!_entityTree) {
|
||||
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
||||
|
||||
|
||||
//if there is no local entity entity tree, no existing velocity, use 0.
|
||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if(cost > _currentAvatarEnergy) {
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
return QUuid();
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
|
||||
|
||||
return id;
|
||||
}
|
||||
// If we have a local entity tree set, then also update it.
|
||||
|
@ -268,8 +266,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
return;
|
||||
}
|
||||
//existing entity, retrieve old velocity for check down below
|
||||
oldVelocity = entity->getVelocity().length();
|
||||
|
||||
oldVelocity = entity->getVelocity().length();
|
||||
|
||||
if (!scriptSideProperties.parentIDChanged()) {
|
||||
properties.setParentID(entity->getParentID());
|
||||
}
|
||||
|
@ -284,16 +282,18 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
}
|
||||
}
|
||||
properties = convertLocationFromScriptSemantics(properties);
|
||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||
|
||||
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if(cost > _currentAvatarEnergy) {
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
updatedEntity = false;
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
emit debitEnergySource(cost);
|
||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||
if (updatedEntity) {
|
||||
emit debitEnergySource(cost);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -370,15 +370,16 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
|||
_entityTree->withWriteLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
|
||||
|
||||
auto dimensions = entity->getDimensions();
|
||||
float volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
auto density = entity->getDensity();
|
||||
auto velocity = entity->getVelocity().length();
|
||||
float cost = calculateCost(density * volume, velocity, 0);
|
||||
cost *= costMultiplier;
|
||||
|
||||
if(cost > _currentAvatarEnergy) {
|
||||
|
||||
if (cost > _currentAvatarEnergy) {
|
||||
shouldDelete = false;
|
||||
return;
|
||||
} else {
|
||||
//debit the avatar energy and continue
|
||||
|
|
|
@ -103,6 +103,7 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID
|
|||
// our non-pure virtual subclass for now...
|
||||
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) :
|
||||
EntityItem(entityItemID),
|
||||
_previousPosition(getPosition()),
|
||||
_lastSimulated(usecTimestampNow())
|
||||
{
|
||||
_type = EntityTypes::ParticleEffect;
|
||||
|
@ -623,7 +624,8 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
|||
}
|
||||
|
||||
// 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();
|
||||
particle.lifetime += timeLeftInFrame;
|
||||
|
||||
|
@ -637,15 +639,16 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
|||
|
||||
_timeUntilNextEmit -= timeLeftInFrame;
|
||||
}
|
||||
_previousPosition = getPosition();
|
||||
}
|
||||
|
||||
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() {
|
||||
ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle(const glm::vec3& position) {
|
||||
Particle particle;
|
||||
|
||||
|
||||
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
||||
if (getEmitterShouldTrail()) {
|
||||
particle.position = getPosition();
|
||||
particle.position = position;
|
||||
}
|
||||
// Position, velocity, and acceleration
|
||||
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
||||
|
|
|
@ -227,7 +227,7 @@ protected:
|
|||
|
||||
bool isAnimatingSomething() const;
|
||||
|
||||
Particle createParticle();
|
||||
Particle createParticle(const glm::vec3& position);
|
||||
void stepSimulation(float deltaTime);
|
||||
void integrateParticle(Particle& particle, float deltaTime);
|
||||
|
||||
|
@ -275,7 +275,7 @@ protected:
|
|||
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
||||
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
||||
|
||||
|
||||
glm::vec3 _previousPosition;
|
||||
quint64 _lastSimulated { 0 };
|
||||
bool _isEmitting { true };
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ private:
|
|||
doneCurrent();
|
||||
|
||||
getContextObject()->moveToThread(QCoreApplication::instance()->thread());
|
||||
_thread.quit();
|
||||
_cond.wakeOne();
|
||||
}
|
||||
|
||||
|
@ -228,7 +229,7 @@ private:
|
|||
|
||||
_quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y));
|
||||
|
||||
{
|
||||
try {
|
||||
PROFILE_RANGE("qml_render")
|
||||
TexturePtr texture = _textures.getNextTexture();
|
||||
_fbo->Bind(Framebuffer::Target::Draw);
|
||||
|
@ -245,8 +246,10 @@ private:
|
|||
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
|
||||
_quickWindow->resetOpenGLState();
|
||||
_escrow.submit(GetName(*texture));
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
} catch (std::runtime_error& error) {
|
||||
qWarning() << "Failed to render QML " << error.what();
|
||||
}
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
}
|
||||
|
||||
void aboutToQuit() {
|
||||
|
@ -321,7 +324,7 @@ OffscreenQmlSurface::~OffscreenQmlSurface() {
|
|||
|
||||
void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||
_renderer = new OffscreenQmlRenderer(this, shareContext);
|
||||
|
||||
_renderer->_renderControl->_renderWindow = _proxyWindow;
|
||||
// Create a QML engine.
|
||||
_qmlEngine = new QQmlEngine;
|
||||
if (!_qmlEngine->incubationController()) {
|
||||
|
@ -610,7 +613,10 @@ bool OffscreenQmlSurface::isPaused() const {
|
|||
}
|
||||
|
||||
void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
|
||||
_renderer->_renderControl->_renderWindow = window;
|
||||
_proxyWindow = window;
|
||||
if (_renderer && _renderer->_renderControl) {
|
||||
_renderer->_renderControl->_renderWindow = window;
|
||||
}
|
||||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::getEventHandler() {
|
||||
|
|
|
@ -95,7 +95,7 @@ private:
|
|||
bool _paused{ true };
|
||||
uint8_t _maxFps{ 60 };
|
||||
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
|
||||
|
||||
QWindow* _proxyWindow { nullptr };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
47
libraries/gpu/src/gpu/Paint.slh
Normal file
47
libraries/gpu/src/gpu/Paint.slh
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!
|
||||
// Paint.slh
|
||||
// libraries/gpu/src
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/19/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Fragment shader utility functions. Will fail compilation in vertex shaders.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
!>
|
||||
<@if not GPU_PAINT_SLH@>
|
||||
<@def GPU_PAINT_SLH@>
|
||||
|
||||
float paintStripe(float value, float offset, float scale, float edge) {
|
||||
float width = fwidth(value);
|
||||
float normalizedWidth = width * scale;
|
||||
|
||||
float x0 = (value + offset) * scale - normalizedWidth / 2;
|
||||
float x1 = x0 + normalizedWidth;
|
||||
|
||||
float balance = 1.0 - edge;
|
||||
float i0 = edge * floor(x0) + max(0.0, fract(x0) - balance);
|
||||
float i1 = edge * floor(x1) + max(0.0, fract(x1) - balance);
|
||||
float strip = (i1 - i0) / normalizedWidth;
|
||||
|
||||
return clamp(strip, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float paintGrid(vec2 value, vec2 offset, vec2 scale, vec2 edge) {
|
||||
return max(
|
||||
paintStripe(value.x, offset.x, scale.x, edge.x),
|
||||
paintStripe(value.y, offset.y, scale.y, edge.y));
|
||||
}
|
||||
|
||||
float paintGridMajor(vec2 value, vec2 offset, vec2 scale, vec2 edge) {
|
||||
return paintGrid(value, offset, scale, edge);
|
||||
}
|
||||
|
||||
float paintGridMajorMinor(vec2 value, vec4 offset, vec4 scale, vec4 edge) {
|
||||
return max(
|
||||
paintGrid(value, offset.xy, scale.xy, edge.xy),
|
||||
paintGrid(value, offset.zw, scale.zw, edge.zw));
|
||||
}
|
||||
|
||||
<@endif@>
|
|
@ -57,7 +57,7 @@ void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) {
|
|||
_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 result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
||||
if (!result.second) {
|
||||
|
@ -70,7 +70,7 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic
|
|||
eraseMouseClicked();
|
||||
}
|
||||
|
||||
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event) {
|
||||
auto input = _inputDevice->makeInput((Qt::MouseButton) event->button());
|
||||
_inputDevice->_buttonPressedMap.erase(input.getChannel());
|
||||
|
||||
|
@ -89,7 +89,7 @@ void KeyboardMouseDevice::eraseMouseClicked() {
|
|||
_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 currentMove = currentPos - _lastCursor;
|
||||
|
||||
|
|
|
@ -75,9 +75,9 @@ public:
|
|||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void eraseMouseClicked();
|
||||
|
||||
void touchBeginEvent(const QTouchEvent* event);
|
||||
|
|
|
@ -78,6 +78,8 @@ public:
|
|||
|
||||
glm::vec3 getLinearVelocity() const;
|
||||
|
||||
float getCapsuleRadius() const { return _radius; }
|
||||
|
||||
enum class State {
|
||||
Ground = 0,
|
||||
Takeoff,
|
||||
|
|
|
@ -24,8 +24,7 @@ class QImage;
|
|||
|
||||
enum Eye {
|
||||
Left,
|
||||
Right,
|
||||
Mono
|
||||
Right
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -95,10 +94,15 @@ public:
|
|||
}
|
||||
|
||||
// Stereo specific methods
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
return baseProjection;
|
||||
}
|
||||
|
||||
virtual glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const {
|
||||
return baseProjection;
|
||||
}
|
||||
|
||||
|
||||
// Fetch the most recently displayed image as a QImage
|
||||
virtual QImage getScreenshot() const = 0;
|
||||
|
||||
|
|
|
@ -22,17 +22,19 @@
|
|||
#include "TextureCache.h"
|
||||
#include "RenderUtilsLogging.h"
|
||||
|
||||
#include "standardTransformPNTC_vert.h"
|
||||
#include "standardDrawTexture_frag.h"
|
||||
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
#include "model/TextureMap.h"
|
||||
|
||||
#include "standardTransformPNTC_vert.h"
|
||||
#include "standardDrawTexture_frag.h"
|
||||
|
||||
#include "simple_vert.h"
|
||||
#include "simple_textured_frag.h"
|
||||
#include "simple_textured_emisive_frag.h"
|
||||
|
||||
#include "grid_frag.h"
|
||||
|
||||
//#define WANT_DEBUG
|
||||
|
||||
const int GeometryCache::UNKNOWN_ID = -1;
|
||||
|
@ -564,186 +566,49 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch) {
|
|||
renderWireShape(batch, Sphere);
|
||||
}
|
||||
|
||||
void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
|
||||
int majorRows, int majorCols, float majorEdge,
|
||||
int minorRows, int minorCols, float minorEdge,
|
||||
const glm::vec4& color, bool isLayered, int id) {
|
||||
static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f);
|
||||
static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f);
|
||||
|
||||
void GeometryCache::renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color) {
|
||||
IntPair key(xDivisions, yDivisions);
|
||||
Vec3Pair colorKey(glm::vec3(color.x, color.y, yDivisions), glm::vec3(color.z, color.y, xDivisions));
|
||||
|
||||
int vertices = (xDivisions + 1 + yDivisions + 1) * 2;
|
||||
if (!_gridBuffers.contains(key)) {
|
||||
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
float* vertexData = new float[vertices * 2];
|
||||
float* vertex = vertexData;
|
||||
|
||||
for (int i = 0; i <= xDivisions; i++) {
|
||||
float x = (float)i / xDivisions;
|
||||
|
||||
*(vertex++) = x;
|
||||
*(vertex++) = 0.0f;
|
||||
|
||||
*(vertex++) = x;
|
||||
*(vertex++) = 1.0f;
|
||||
}
|
||||
for (int i = 0; i <= yDivisions; i++) {
|
||||
float y = (float)i / yDivisions;
|
||||
|
||||
*(vertex++) = 0.0f;
|
||||
*(vertex++) = y;
|
||||
|
||||
*(vertex++) = 1.0f;
|
||||
*(vertex++) = y;
|
||||
}
|
||||
|
||||
verticesBuffer->append(sizeof(float) * vertices * 2, (gpu::Byte*) vertexData);
|
||||
delete[] vertexData;
|
||||
|
||||
_gridBuffers[key] = verticesBuffer;
|
||||
}
|
||||
|
||||
if (!_gridColors.contains(colorKey)) {
|
||||
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
_gridColors[colorKey] = colorBuffer;
|
||||
|
||||
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
||||
((int(color.y * 255.0f) & 0xFF) << 8) |
|
||||
((int(color.z * 255.0f) & 0xFF) << 16) |
|
||||
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||
|
||||
int* colorData = new int[vertices];
|
||||
int* colorDataAt = colorData;
|
||||
|
||||
for(int v = 0; v < vertices; v++) {
|
||||
*(colorDataAt++) = compactColor;
|
||||
}
|
||||
|
||||
colorBuffer->append(sizeof(int) * vertices, (gpu::Byte*) colorData);
|
||||
delete[] colorData;
|
||||
}
|
||||
gpu::BufferPointer verticesBuffer = _gridBuffers[key];
|
||||
gpu::BufferPointer colorBuffer = _gridColors[colorKey];
|
||||
|
||||
const int VERTICES_SLOT = 0;
|
||||
const int COLOR_SLOT = 1;
|
||||
auto streamFormat = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
|
||||
streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
||||
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
|
||||
gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
|
||||
gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
|
||||
|
||||
batch.setInputFormat(streamFormat);
|
||||
batch.setInputBuffer(VERTICES_SLOT, verticesView);
|
||||
batch.setInputBuffer(COLOR_SLOT, colorView);
|
||||
batch.draw(gpu::LINES, vertices, 0);
|
||||
}
|
||||
|
||||
// TODO: why do we seem to create extra BatchItemDetails when we resize the window?? what's that??
|
||||
void GeometryCache::renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id) {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(renderutils) << "GeometryCache::renderGrid(x["<<x<<"], "
|
||||
"y["<<y<<"],"
|
||||
"w["<<width<<"],"
|
||||
"h["<<height<<"],"
|
||||
"rows["<<rows<<"],"
|
||||
"cols["<<cols<<"],"
|
||||
" id:"<<id<<")...";
|
||||
#endif
|
||||
|
||||
bool registered = (id != UNKNOWN_ID);
|
||||
Vec3Pair key(glm::vec3(x, y, width), glm::vec3(height, rows, cols));
|
||||
Vec3Pair colorKey(glm::vec3(color.x, color.y, rows), glm::vec3(color.z, color.y, cols));
|
||||
Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);
|
||||
Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);
|
||||
Vec2FloatPairPair key(majorKey, minorKey);
|
||||
|
||||
int vertices = (cols + 1 + rows + 1) * 2;
|
||||
if ((registered && (!_registeredAlternateGridBuffers.contains(id) || _lastRegisteredAlternateGridBuffers[id] != key))
|
||||
|| (!registered && !_alternateGridBuffers.contains(key))) {
|
||||
// Make the gridbuffer
|
||||
if ((registered && (!_registeredGridBuffers.contains(id) || _lastRegisteredGridBuffer[id] != key)) ||
|
||||
(!registered && !_gridBuffers.contains(key))) {
|
||||
GridSchema gridSchema;
|
||||
GridBuffer gridBuffer = std::make_shared<gpu::Buffer>(sizeof(GridSchema), (const gpu::Byte*) &gridSchema);
|
||||
|
||||
if (registered && _registeredAlternateGridBuffers.contains(id)) {
|
||||
_registeredAlternateGridBuffers[id].reset();
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(renderutils) << "renderGrid()... RELEASING REGISTERED VERTICES BUFFER";
|
||||
#endif
|
||||
if (registered && _registeredGridBuffers.contains(id)) {
|
||||
gridBuffer = _registeredGridBuffers[id];
|
||||
}
|
||||
|
||||
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
if (registered) {
|
||||
_registeredAlternateGridBuffers[id] = verticesBuffer;
|
||||
_lastRegisteredAlternateGridBuffers[id] = key;
|
||||
_registeredGridBuffers[id] = gridBuffer;
|
||||
_lastRegisteredGridBuffer[id] = key;
|
||||
} else {
|
||||
_alternateGridBuffers[key] = verticesBuffer;
|
||||
_gridBuffers[key] = gridBuffer;
|
||||
}
|
||||
|
||||
float* vertexData = new float[vertices * 2];
|
||||
float* vertex = vertexData;
|
||||
|
||||
int dx = width / cols;
|
||||
int dy = height / rows;
|
||||
int tx = x;
|
||||
int ty = y;
|
||||
|
||||
// Draw horizontal grid lines
|
||||
for (int i = rows + 1; --i >= 0; ) {
|
||||
*(vertex++) = x;
|
||||
*(vertex++) = ty;
|
||||
|
||||
*(vertex++) = x + width;
|
||||
*(vertex++) = ty;
|
||||
|
||||
ty += dy;
|
||||
}
|
||||
// Draw vertical grid lines
|
||||
for (int i = cols + 1; --i >= 0; ) {
|
||||
*(vertex++) = tx;
|
||||
*(vertex++) = y;
|
||||
|
||||
*(vertex++) = tx;
|
||||
*(vertex++) = y + height;
|
||||
tx += dx;
|
||||
}
|
||||
|
||||
verticesBuffer->append(sizeof(float) * vertices * 2, (gpu::Byte*) vertexData);
|
||||
delete[] vertexData;
|
||||
gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
|
||||
gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;
|
||||
gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;
|
||||
gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;
|
||||
gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;
|
||||
gridBuffer.edit<GridSchema>().edge = glm::vec4(glm::vec2(majorEdge),
|
||||
// If rows or columns are not set, do not draw minor gridlines
|
||||
glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));
|
||||
}
|
||||
|
||||
if (!_gridColors.contains(colorKey)) {
|
||||
auto colorBuffer = std::make_shared<gpu::Buffer>();
|
||||
_gridColors[colorKey] = colorBuffer;
|
||||
// Set the grid pipeline
|
||||
useGridPipeline(batch, registered ? _registeredGridBuffers[id] : _gridBuffers[key], isLayered);
|
||||
|
||||
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
||||
((int(color.y * 255.0f) & 0xFF) << 8) |
|
||||
((int(color.z * 255.0f) & 0xFF) << 16) |
|
||||
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||
|
||||
int* colorData = new int[vertices];
|
||||
int* colorDataAt = colorData;
|
||||
|
||||
|
||||
for(int v = 0; v < vertices; v++) {
|
||||
*(colorDataAt++) = compactColor;
|
||||
}
|
||||
|
||||
colorBuffer->append(sizeof(int) * vertices, (gpu::Byte*) colorData);
|
||||
delete[] colorData;
|
||||
}
|
||||
gpu::BufferPointer verticesBuffer = registered ? _registeredAlternateGridBuffers[id] : _alternateGridBuffers[key];
|
||||
|
||||
gpu::BufferPointer colorBuffer = _gridColors[colorKey];
|
||||
|
||||
const int VERTICES_SLOT = 0;
|
||||
const int COLOR_SLOT = 1;
|
||||
auto streamFormat = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
|
||||
streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
|
||||
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
|
||||
gpu::BufferView verticesView(verticesBuffer, 0, verticesBuffer->getSize(), streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
|
||||
gpu::BufferView colorView(colorBuffer, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
|
||||
|
||||
batch.setInputFormat(streamFormat);
|
||||
batch.setInputBuffer(VERTICES_SLOT, verticesView);
|
||||
batch.setInputBuffer(COLOR_SLOT, colorView);
|
||||
batch.draw(gpu::LINES, vertices, 0);
|
||||
renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);
|
||||
}
|
||||
|
||||
void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color) {
|
||||
|
@ -1772,7 +1637,6 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
|||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
|
||||
// enable decal blend
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
|
||||
|
@ -1792,6 +1656,30 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
|
|||
}
|
||||
}
|
||||
|
||||
void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) {
|
||||
if (!_gridPipeline) {
|
||||
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(grid_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
_gridSlot = program->getBuffers().findLocation("gridBuffer");
|
||||
|
||||
auto stateLayered = std::make_shared<gpu::State>();
|
||||
stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);
|
||||
|
||||
auto state = std::make_shared<gpu::State>(stateLayered->getValues());
|
||||
const float DEPTH_BIAS = 0.001f;
|
||||
state->setDepthBias(DEPTH_BIAS);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
_gridPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::PipelinePointer pipeline = isLayered ? _gridPipelineLayered : _gridPipeline;
|
||||
batch.setPipeline(pipeline);
|
||||
batch.setUniformBuffer(_gridSlot, gridBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SimpleProgramKey {
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
|
||||
class SimpleProgramKey;
|
||||
|
||||
typedef glm::vec3 Vec3Key;
|
||||
|
||||
typedef QPair<glm::vec2, float> Vec2FloatPair;
|
||||
typedef QPair<Vec2FloatPair, Vec2FloatPair> Vec2FloatPairPair;
|
||||
typedef QPair<glm::vec2, glm::vec2> Vec2Pair;
|
||||
typedef QPair<Vec2Pair, Vec2Pair> Vec2PairPair;
|
||||
typedef QPair<glm::vec3, glm::vec3> Vec3Pair;
|
||||
|
@ -43,9 +43,10 @@ typedef QPair<Vec3Pair, Vec4Pair> Vec3PairVec4Pair;
|
|||
typedef QPair<Vec4Pair, glm::vec4> Vec4PairVec4;
|
||||
typedef QPair<Vec4Pair, Vec4Pair> Vec4PairVec4Pair;
|
||||
|
||||
inline uint qHash(const glm::vec2& v, uint seed) {
|
||||
inline uint qHash(const Vec2FloatPairPair& v, uint seed) {
|
||||
// multiply by prime numbers greater than the possible size
|
||||
return qHash(v.x + 5009 * v.y, seed);
|
||||
return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.second +
|
||||
5021 * v.second.first.x + 5023 * v.second.first.y + 5039 * v.second.second);
|
||||
}
|
||||
|
||||
inline uint qHash(const Vec2Pair& v, uint seed) {
|
||||
|
@ -203,8 +204,14 @@ public:
|
|||
void renderWireSphere(gpu::Batch& batch);
|
||||
size_t getSphereTriangleCount();
|
||||
|
||||
void renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color);
|
||||
void renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID);
|
||||
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
|
||||
int majorRows, int majorCols, float majorEdge,
|
||||
int minorRows, int minorCols, float minorEdge,
|
||||
const glm::vec4& color, bool isLayered, int id = UNKNOWN_ID);
|
||||
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
|
||||
int rows, int cols, float edge, const glm::vec4& color, bool isLayered, int id = UNKNOWN_ID) {
|
||||
renderGrid(batch, minCorner, maxCorner, rows, cols, edge, 0, 0, 0.0f, color, isLayered, id);
|
||||
}
|
||||
|
||||
void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);
|
||||
|
||||
|
@ -310,6 +317,19 @@ private:
|
|||
gpu::BufferPointer _shapeVertices{ std::make_shared<gpu::Buffer>() };
|
||||
gpu::BufferPointer _shapeIndices{ std::make_shared<gpu::Buffer>() };
|
||||
|
||||
class GridSchema {
|
||||
public:
|
||||
// data is arranged as majorRow, majorCol, minorRow, minorCol
|
||||
glm::vec4 period;
|
||||
glm::vec4 offset;
|
||||
glm::vec4 edge;
|
||||
};
|
||||
using GridBuffer = gpu::BufferView;
|
||||
void useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered);
|
||||
gpu::PipelinePointer _gridPipeline;
|
||||
gpu::PipelinePointer _gridPipelineLayered;
|
||||
int _gridSlot;
|
||||
|
||||
class BatchItemDetails {
|
||||
public:
|
||||
static int population;
|
||||
|
@ -366,11 +386,9 @@ private:
|
|||
QHash<Vec3PairVec2Pair, BatchItemDetails> _dashedLines;
|
||||
QHash<int, BatchItemDetails> _registeredDashedLines;
|
||||
|
||||
QHash<IntPair, gpu::BufferPointer> _gridBuffers;
|
||||
QHash<Vec3Pair, gpu::BufferPointer> _alternateGridBuffers;
|
||||
QHash<int, gpu::BufferPointer> _registeredAlternateGridBuffers;
|
||||
QHash<int, Vec3Pair> _lastRegisteredAlternateGridBuffers;
|
||||
QHash<Vec3Pair, gpu::BufferPointer> _gridColors;
|
||||
QHash<int, Vec2FloatPairPair> _lastRegisteredGridBuffer;
|
||||
QHash<Vec2FloatPairPair, GridBuffer> _gridBuffers;
|
||||
QHash<int, GridBuffer> _registeredGridBuffers;
|
||||
|
||||
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
|
||||
|
||||
|
|
|
@ -92,15 +92,15 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
_scaledToFit = false;
|
||||
}
|
||||
|
||||
const float METERS_PER_MILLIMETER = 0.01f;
|
||||
const float SCALE_CHANGE_EPSILON = 0.01f;
|
||||
|
||||
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;
|
||||
if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) {
|
||||
assert(false);
|
||||
}
|
||||
initJointTransforms();
|
||||
simulate(0.0f, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
44
libraries/render-utils/src/grid.slf
Normal file
44
libraries/render-utils/src/grid.slf
Normal file
|
@ -0,0 +1,44 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// grid.slf
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/16/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Paint.slh@>
|
||||
|
||||
struct Grid {
|
||||
vec4 period;
|
||||
vec4 offset;
|
||||
vec4 edge;
|
||||
};
|
||||
|
||||
uniform gridBuffer { Grid grid; };
|
||||
Grid getGrid() { return grid; };
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
in vec4 varColor;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
Grid grid = getGrid();
|
||||
|
||||
float alpha;
|
||||
if (grid.edge.z == 0.0) {
|
||||
alpha = paintGrid(varTexCoord0, grid.offset.xy, grid.period.xy, grid.edge.xy);
|
||||
} else {
|
||||
alpha = paintGridMajorMinor(varTexCoord0, grid.offset, grid.period, grid.edge);
|
||||
}
|
||||
if (alpha == 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
outFragColor = vec4(varColor.xyz, varColor.w * alpha);
|
||||
}
|
|
@ -45,8 +45,11 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi
|
|||
// stereo option isn't set from script, this comes from sound metadata or filename
|
||||
AudioInjectorOptions optionsCopy = injectorOptions;
|
||||
optionsCopy.stereo = sound->isStereo();
|
||||
|
||||
return new ScriptAudioInjector(AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface));
|
||||
auto injector = AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface);
|
||||
if (!injector) {
|
||||
return NULL;
|
||||
}
|
||||
return new ScriptAudioInjector(injector);
|
||||
|
||||
} else {
|
||||
qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object.";
|
||||
|
|
|
@ -29,10 +29,9 @@ MouseEvent::MouseEvent() :
|
|||
}
|
||||
|
||||
|
||||
MouseEvent::MouseEvent(const QMouseEvent& event, const unsigned int deviceID) :
|
||||
MouseEvent::MouseEvent(const QMouseEvent& event) :
|
||||
x(event.x()),
|
||||
y(event.y()),
|
||||
deviceID(deviceID),
|
||||
isLeftButton(event.buttons().testFlag(Qt::LeftButton)),
|
||||
isRightButton(event.buttons().testFlag(Qt::RightButton)),
|
||||
isMiddleButton(event.buttons().testFlag(Qt::MiddleButton)),
|
||||
|
@ -66,7 +65,6 @@ QScriptValue MouseEvent::toScriptValue(QScriptEngine* engine, const MouseEvent&
|
|||
obj.setProperty("x", event.x);
|
||||
obj.setProperty("y", event.y);
|
||||
obj.setProperty("button", event.button);
|
||||
obj.setProperty("deviceID", event.deviceID);
|
||||
obj.setProperty("isLeftButton", event.isLeftButton);
|
||||
obj.setProperty("isRightButton", event.isRightButton);
|
||||
obj.setProperty("isMiddleButton", event.isMiddleButton);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
class MouseEvent {
|
||||
public:
|
||||
MouseEvent();
|
||||
MouseEvent(const QMouseEvent& event, const unsigned int deviceID = 0);
|
||||
MouseEvent(const QMouseEvent& event);
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
||||
|
@ -26,7 +26,6 @@ public:
|
|||
|
||||
int x;
|
||||
int y;
|
||||
unsigned int deviceID;
|
||||
QString button;
|
||||
bool isLeftButton;
|
||||
bool isRightButton;
|
||||
|
|
|
@ -29,20 +29,6 @@ namespace Cursor {
|
|||
Source getType() const {
|
||||
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;
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
namespace Cursor {
|
||||
enum class Source {
|
||||
MOUSE,
|
||||
LEFT_HAND,
|
||||
RIGHT_HAND,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
|
@ -33,9 +31,6 @@ namespace Cursor {
|
|||
class Instance {
|
||||
public:
|
||||
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 uint16_t getIcon() const;
|
||||
private:
|
||||
|
|
|
@ -11,22 +11,10 @@
|
|||
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
|
||||
return _desiredFramebufferSize;
|
||||
}
|
||||
|
||||
glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
return _eyeProjections[eye];
|
||||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::resetSensors() {
|
||||
ovr_RecenterPose(_session);
|
||||
}
|
||||
|
||||
glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
|
||||
return glm::translate(mat4(), toGlm(_eyeOffsets[eye]));
|
||||
}
|
||||
|
||||
glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
||||
static uint32_t lastFrameSeen = 0;
|
||||
auto displayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
|
||||
|
@ -70,7 +58,7 @@ void OculusBaseDisplayPlugin::customizeContext() {
|
|||
glewExperimental = true;
|
||||
GLenum err = glewInit();
|
||||
glGetError();
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
HmdDisplayPlugin::customizeContext();
|
||||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::init() {
|
||||
|
@ -88,37 +76,31 @@ void OculusBaseDisplayPlugin::activate() {
|
|||
qFatal("Failed to acquire HMD");
|
||||
}
|
||||
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
HmdDisplayPlugin::activate();
|
||||
|
||||
_hmdDesc = ovr_GetHmdDesc(_session);
|
||||
|
||||
_ipd = ovr_GetFloat(_session, OVR_KEY_IPD, _ipd);
|
||||
|
||||
glm::uvec2 eyeSizes[2];
|
||||
_viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
||||
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
|
||||
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]);
|
||||
ovrMatrix4f ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
||||
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||
_eyeOffsets[eye] = glm::translate(mat4(), toGlm(erd.HmdToEyeViewOffset));
|
||||
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f));
|
||||
_viewScaleDesc.HmdToEyeViewOffset[eye] = erd.HmdToEyeViewOffset;
|
||||
});
|
||||
ovrFovPort combined = _eyeFovs[Left];
|
||||
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
|
||||
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
|
||||
ovrMatrix4f ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
auto combinedFov = _eyeFovs[0];
|
||||
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan);
|
||||
_cullingProjection = toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));
|
||||
|
||||
|
||||
_desiredFramebufferSize = uvec2(
|
||||
_renderTargetSize = uvec2(
|
||||
eyeSizes[0].x + eyeSizes[1].x,
|
||||
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||
|
||||
|
@ -144,20 +126,8 @@ void OculusBaseDisplayPlugin::activate() {
|
|||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::deactivate() {
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
|
||||
#if (OVR_MAJOR_VERSION >= 6)
|
||||
HmdDisplayPlugin::deactivate();
|
||||
ovr_Destroy(_session);
|
||||
_session = nullptr;
|
||||
ovr_Shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
float OculusBaseDisplayPlugin::getIPD() const {
|
||||
float result = OVR_DEFAULT_IPD;
|
||||
#if (OVR_MAJOR_VERSION >= 6)
|
||||
result = ovr_GetFloat(_session, OVR_KEY_IPD, result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include <display-plugins/WindowOpenGLDisplayPlugin.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <OVR_CAPI_GL.h>
|
||||
|
||||
class OculusBaseDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
||||
public:
|
||||
virtual bool isSupported() const override;
|
||||
|
||||
|
@ -24,25 +24,13 @@ public:
|
|||
virtual void deactivate() override;
|
||||
|
||||
// Stereo specific methods
|
||||
virtual bool isHmd() const override final { return true; }
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override final;
|
||||
virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); }
|
||||
virtual void resetSensors() override final;
|
||||
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override final;
|
||||
virtual float getIPD() const override final;
|
||||
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
|
||||
|
||||
protected:
|
||||
virtual void customizeContext() override;
|
||||
|
||||
protected:
|
||||
ovrVector3f _eyeOffsets[2];
|
||||
|
||||
mat4 _eyeProjections[3];
|
||||
mat4 _compositeEyeProjections[2];
|
||||
uvec2 _desiredFramebufferSize;
|
||||
|
||||
ovrSession _session;
|
||||
ovrGraphicsLuid _luid;
|
||||
float _ipd{ OVR_DEFAULT_IPD };
|
||||
|
@ -50,23 +38,5 @@ protected:
|
|||
ovrFovPort _eyeFovs[2];
|
||||
ovrHmdDesc _hmdDesc;
|
||||
ovrLayerEyeFov _sceneLayer;
|
||||
ovrViewScaleDesc _viewScaleDesc;
|
||||
};
|
||||
|
||||
#if (OVR_MAJOR_VERSION == 6)
|
||||
#define ovr_Create ovrHmd_Create
|
||||
#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL
|
||||
#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL
|
||||
#define ovr_Destroy ovrHmd_Destroy
|
||||
#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet
|
||||
#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture
|
||||
#define ovr_GetFloat ovrHmd_GetFloat
|
||||
#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize
|
||||
#define ovr_GetFrameTiming ovrHmd_GetFrameTiming
|
||||
#define ovr_GetTrackingState ovrHmd_GetTrackingState
|
||||
#define ovr_GetRenderDesc ovrHmd_GetRenderDesc
|
||||
#define ovr_RecenterPose ovrHmd_RecenterPose
|
||||
#define ovr_SubmitFrame ovrHmd_SubmitFrame
|
||||
#define ovr_ConfigureTracking ovrHmd_ConfigureTracking
|
||||
|
||||
#define ovr_GetHmdDesc(X) *X
|
||||
#endif
|
||||
|
|
|
@ -6,150 +6,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OculusDisplayPlugin.h"
|
||||
|
||||
#include <QtOpenGL/QGLWidget>
|
||||
|
||||
// FIXME get rid of this
|
||||
#include <gl/Config.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
|
||||
#if (OVR_MAJOR_VERSION >= 6)
|
||||
|
||||
// A base class for FBO wrappers that need to use the Oculus C
|
||||
// API to manage textures via ovr_CreateSwapTextureSetGL,
|
||||
// ovr_CreateMirrorTextureGL, etc
|
||||
template <typename C>
|
||||
struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {
|
||||
ovrSession session;
|
||||
RiftFramebufferWrapper(const ovrSession& session) : session(session) {
|
||||
color = 0;
|
||||
depth = 0;
|
||||
};
|
||||
|
||||
~RiftFramebufferWrapper() {
|
||||
destroyColor();
|
||||
}
|
||||
|
||||
void Resize(const uvec2 & size) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
this->size = size;
|
||||
initColor();
|
||||
initDone();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void destroyColor() {
|
||||
}
|
||||
|
||||
virtual void initDepth() override final {
|
||||
}
|
||||
};
|
||||
|
||||
// A wrapper for constructing and using a swap texture set,
|
||||
// where each frame you draw to a texture via the FBO,
|
||||
// then submit it and increment to the next texture.
|
||||
// The Oculus SDK manages the creation and destruction of
|
||||
// the textures
|
||||
struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*> {
|
||||
SwapFramebufferWrapper(const ovrHmd & hmd)
|
||||
: RiftFramebufferWrapper(hmd) {
|
||||
}
|
||||
|
||||
|
||||
void Increment() {
|
||||
++color->CurrentIndex;
|
||||
color->CurrentIndex %= color->TextureCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void destroyColor() override {
|
||||
if (color) {
|
||||
ovr_DestroySwapTextureSet(session, color);
|
||||
color = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void initColor() override {
|
||||
destroyColor();
|
||||
|
||||
if (!OVR_SUCCESS(ovr_CreateSwapTextureSetGL(session, GL_SRGB8_ALPHA8, size.x, size.y, &color))) {
|
||||
qFatal("Unable to create swap textures");
|
||||
}
|
||||
|
||||
for (int i = 0; i < color->TextureCount; ++i) {
|
||||
ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i];
|
||||
glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
virtual void initDone() override {
|
||||
}
|
||||
|
||||
virtual void onBind(oglplus::Framebuffer::Target target) override {
|
||||
ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]);
|
||||
glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0);
|
||||
}
|
||||
|
||||
virtual void onUnbind(oglplus::Framebuffer::Target target) override {
|
||||
glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// We use a FBO to wrap the mirror texture because it makes it easier to
|
||||
// render to the screen via glBlitFramebuffer
|
||||
struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
|
||||
MirrorFramebufferWrapper(const ovrHmd & hmd)
|
||||
: RiftFramebufferWrapper(hmd) { }
|
||||
|
||||
private:
|
||||
virtual void destroyColor() override {
|
||||
if (color) {
|
||||
ovr_DestroyMirrorTexture(session, (ovrTexture*)color);
|
||||
color = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void initColor() override {
|
||||
destroyColor();
|
||||
ovrResult result = ovr_CreateMirrorTextureGL(session, GL_SRGB8_ALPHA8, size.x, size.y, (ovrTexture**)&color);
|
||||
Q_ASSERT(OVR_SUCCESS(result));
|
||||
}
|
||||
|
||||
void initDone() override {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color->OGL.TexId, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
||||
|
||||
static const QString MONO_PREVIEW = "Mono Preview";
|
||||
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
||||
static const bool DEFAULT_MONO_VIEW = true;
|
||||
|
||||
void OculusDisplayPlugin::activate() {
|
||||
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
||||
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
|
||||
[this](bool clicked) {
|
||||
_monoPreview = clicked;
|
||||
_container->setBoolSetting("monoPreview", _monoPreview);
|
||||
}, true, _monoPreview);
|
||||
_container->removeMenu(FRAMERATE);
|
||||
OculusBaseDisplayPlugin::activate();
|
||||
}
|
||||
|
||||
|
@ -182,27 +43,8 @@ void OculusDisplayPlugin::internalPresent() {
|
|||
}
|
||||
|
||||
using namespace oglplus;
|
||||
// Need to make sure only the display plugin is responsible for
|
||||
// controlling vsync
|
||||
wglSwapIntervalEXT(0);
|
||||
|
||||
// screen preview mirroring
|
||||
if (_enablePreview) {
|
||||
auto windowSize = toGlm(_window->size());
|
||||
if (_monoPreview) {
|
||||
Context::Viewport(windowSize.x * 2, windowSize.y);
|
||||
Context::Scissor(0, windowSize.y, windowSize.x, windowSize.y);
|
||||
} else {
|
||||
Context::Viewport(windowSize.x, windowSize.y);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
GLenum err = glGetError();
|
||||
Q_ASSERT(0 == err);
|
||||
drawUnitQuad();
|
||||
}
|
||||
|
||||
const auto& size = _sceneFbo->size;
|
||||
_sceneFbo->Bound([&] {
|
||||
auto size = _sceneFbo->size;
|
||||
Context::Viewport(size.x, size.y);
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
//glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
|
@ -225,27 +67,17 @@ void OculusDisplayPlugin::internalPresent() {
|
|||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = eyePoses.second;
|
||||
|
||||
{
|
||||
ovrViewScaleDesc viewScaleDesc;
|
||||
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
||||
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
|
||||
viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1];
|
||||
|
||||
ovrLayerHeader* layers = &_sceneLayer.Header;
|
||||
ovrResult result = ovr_SubmitFrame(_session, frameIndex, &viewScaleDesc, &layers, 1);
|
||||
ovrResult result = ovr_SubmitFrame(_session, frameIndex, &_viewScaleDesc, &layers, 1);
|
||||
if (!OVR_SUCCESS(result)) {
|
||||
qDebug() << result;
|
||||
}
|
||||
}
|
||||
_sceneFbo->Increment();
|
||||
|
||||
/*
|
||||
The swapbuffer call here is only required if we want to mirror the content to the screen.
|
||||
However, it should only be done if we can reliably disable v-sync on the mirror surface,
|
||||
otherwise the swapbuffer delay will interefere with the framerate of the headset
|
||||
*/
|
||||
if (_enablePreview) {
|
||||
swapBuffers();
|
||||
}
|
||||
// Handle mirroring to screen in base class
|
||||
HmdDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
|
|
|
@ -8,16 +8,69 @@
|
|||
|
||||
#include "OculusHelpers.h"
|
||||
|
||||
// A wrapper for constructing and using a swap texture set,
|
||||
// where each frame you draw to a texture via the FBO,
|
||||
// then submit it and increment to the next texture.
|
||||
// The Oculus SDK manages the creation and destruction of
|
||||
// the textures
|
||||
|
||||
namespace Oculus {
|
||||
ovrHmd _hmd;
|
||||
ovrEyeRenderDesc _eyeRenderDescs[2];
|
||||
ovrPosef _eyePoses[2];
|
||||
ovrVector3f _eyeOffsets[2];
|
||||
ovrFovPort _eyeFovs[2];
|
||||
mat4 _eyeProjections[2];
|
||||
mat4 _compositeEyeProjections[2];
|
||||
uvec2 _desiredFramebufferSize;
|
||||
SwapFramebufferWrapper::SwapFramebufferWrapper(const ovrSession& session)
|
||||
: _session(session) {
|
||||
color = nullptr;
|
||||
depth = nullptr;
|
||||
}
|
||||
|
||||
SwapFramebufferWrapper::~SwapFramebufferWrapper() {
|
||||
destroyColor();
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::Increment() {
|
||||
++color->CurrentIndex;
|
||||
color->CurrentIndex %= color->TextureCount;
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::Resize(const uvec2 & size) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
this->size = size;
|
||||
initColor();
|
||||
initDone();
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::destroyColor() {
|
||||
if (color) {
|
||||
ovr_DestroySwapTextureSet(_session, color);
|
||||
color = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::initColor() {
|
||||
destroyColor();
|
||||
|
||||
if (!OVR_SUCCESS(ovr_CreateSwapTextureSetGL(_session, GL_SRGB8_ALPHA8, size.x, size.y, &color))) {
|
||||
qFatal("Unable to create swap textures");
|
||||
}
|
||||
|
||||
for (int i = 0; i < color->TextureCount; ++i) {
|
||||
ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i];
|
||||
glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::initDone() {
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::onBind(oglplus::Framebuffer::Target target) {
|
||||
ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]);
|
||||
glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0);
|
||||
}
|
||||
|
||||
void SwapFramebufferWrapper::onUnbind(oglplus::Framebuffer::Target target) {
|
||||
glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#if (OVR_MAJOR_VERSION < 6)
|
||||
#define OVR_SUCCESS(x) x
|
||||
#endif
|
||||
#include <gl/OglplusHelpers.h>
|
||||
|
||||
// Convenience method for looping over each eye with a lambda
|
||||
template <typename Function>
|
||||
|
@ -87,3 +85,26 @@ inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) {
|
|||
result.Position = ovrFromGlm(translation);
|
||||
return result;
|
||||
}
|
||||
|
||||
// A wrapper for constructing and using a swap texture set,
|
||||
// where each frame you draw to a texture via the FBO,
|
||||
// then submit it and increment to the next texture.
|
||||
// The Oculus SDK manages the creation and destruction of
|
||||
// the textures
|
||||
struct SwapFramebufferWrapper : public FramebufferWrapper<ovrSwapTextureSet*, void*> {
|
||||
SwapFramebufferWrapper(const ovrSession& session);
|
||||
~SwapFramebufferWrapper();
|
||||
void Increment();
|
||||
void Resize(const uvec2 & size);
|
||||
protected:
|
||||
void initColor() override final;
|
||||
void initDepth() override final {}
|
||||
void initDone() override final;
|
||||
void onBind(oglplus::Framebuffer::Target target) override final;
|
||||
void onUnbind(oglplus::Framebuffer::Target target) override final;
|
||||
|
||||
void destroyColor();
|
||||
|
||||
private:
|
||||
ovrSession _session;
|
||||
};
|
||||
|
|
|
@ -31,29 +31,15 @@ const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5) (Legacy)");
|
|||
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() {
|
||||
}
|
||||
|
||||
uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const {
|
||||
return _desiredFramebufferSize;
|
||||
}
|
||||
|
||||
glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
return _eyeProjections[eye];
|
||||
}
|
||||
|
||||
void OculusLegacyDisplayPlugin::resetSensors() {
|
||||
ovrHmd_RecenterPose(_hmd);
|
||||
}
|
||||
|
||||
glm::mat4 OculusLegacyDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
|
||||
return toGlm(_eyePoses[eye]);
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
||||
static uint32_t lastFrameSeen = 0;
|
||||
if (frameIndex > lastFrameSeen) {
|
||||
Lock lock(_mutex);
|
||||
_trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds());
|
||||
ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState);
|
||||
lastFrameSeen = frameIndex;
|
||||
}
|
||||
return toGlm(_trackingState.HeadPose.ThePose);
|
||||
|
@ -87,7 +73,7 @@ bool OculusLegacyDisplayPlugin::isSupported() const {
|
|||
}
|
||||
|
||||
void OculusLegacyDisplayPlugin::activate() {
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
HmdDisplayPlugin::activate();
|
||||
|
||||
if (!(ovr_Initialize(nullptr))) {
|
||||
Q_ASSERT(false);
|
||||
|
@ -100,30 +86,26 @@ void OculusLegacyDisplayPlugin::activate() {
|
|||
qFatal("Failed to acquire HMD");
|
||||
}
|
||||
|
||||
_ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
|
||||
|
||||
glm::uvec2 eyeSizes[2];
|
||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
|
||||
ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
|
||||
ovrMatrix4f ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
||||
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||
_eyeOffsets[eye] = glm::translate(mat4(), toGlm(erd.HmdToEyeViewOffset));
|
||||
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
||||
});
|
||||
ovrFovPort combined = _eyeFovs[Left];
|
||||
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
|
||||
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
|
||||
ovrMatrix4f ovrPerspectiveProjection =
|
||||
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
|
||||
|
||||
_desiredFramebufferSize = uvec2(eyeSizes[0].x + eyeSizes[1].x,
|
||||
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||
auto combinedFov = _eyeFovs[0];
|
||||
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan);
|
||||
_cullingProjection = toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));
|
||||
|
||||
_renderTargetSize = uvec2(
|
||||
eyeSizes[0].x + eyeSizes[1].x,
|
||||
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||
|
||||
if (!ovrHmd_ConfigureTracking(_hmd,
|
||||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) {
|
||||
|
@ -132,13 +114,12 @@ void OculusLegacyDisplayPlugin::activate() {
|
|||
}
|
||||
|
||||
void OculusLegacyDisplayPlugin::deactivate() {
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
HmdDisplayPlugin::deactivate();
|
||||
ovrHmd_Destroy(_hmd);
|
||||
_hmd = nullptr;
|
||||
ovr_Shutdown();
|
||||
}
|
||||
|
||||
|
||||
// DLL based display plugins MUST initialize GLEW inside the DLL code.
|
||||
void OculusLegacyDisplayPlugin::customizeContext() {
|
||||
static std::once_flag once;
|
||||
|
@ -147,7 +128,7 @@ void OculusLegacyDisplayPlugin::customizeContext() {
|
|||
glewInit();
|
||||
glGetError();
|
||||
});
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
HmdDisplayPlugin::customizeContext();
|
||||
#if 0
|
||||
ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig));
|
||||
auto& header = config.Config.Header;
|
||||
|
@ -179,7 +160,7 @@ void OculusLegacyDisplayPlugin::customizeContext() {
|
|||
|
||||
#if 0
|
||||
void OculusLegacyDisplayPlugin::uncustomizeContext() {
|
||||
WindowOpenGLDisplayPlugin::uncustomizeContext();
|
||||
HmdDisplayPlugin::uncustomizeContext();
|
||||
}
|
||||
|
||||
void OculusLegacyDisplayPlugin::internalPresent() {
|
||||
|
@ -200,3 +181,4 @@ float OculusLegacyDisplayPlugin::getTargetFrameRate() {
|
|||
return TARGET_RATE_OculusLegacy;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include <display-plugins/WindowOpenGLDisplayPlugin.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
|||
|
||||
const float TARGET_RATE_OculusLegacy = 75.0f;
|
||||
|
||||
class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
class OculusLegacyDisplayPlugin : public HmdDisplayPlugin {
|
||||
public:
|
||||
OculusLegacyDisplayPlugin();
|
||||
virtual bool isSupported() const override;
|
||||
|
@ -27,12 +27,7 @@ public:
|
|||
virtual int getHmdScreen() const override;
|
||||
|
||||
// Stereo specific methods
|
||||
virtual bool isHmd() const override { return true; }
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
|
||||
virtual void resetSensors() override;
|
||||
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
|
||||
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
|
||||
|
||||
virtual float getTargetFrameRate() override;
|
||||
|
@ -50,12 +45,7 @@ private:
|
|||
ovrHmd _hmd;
|
||||
mutable ovrTrackingState _trackingState;
|
||||
ovrEyeRenderDesc _eyeRenderDescs[2];
|
||||
mutable ovrPosef _eyePoses[2];
|
||||
ovrVector3f _eyeOffsets[2];
|
||||
ovrFovPort _eyeFovs[2];
|
||||
mat4 _eyeProjections[3];
|
||||
mat4 _compositeEyeProjections[2];
|
||||
uvec2 _desiredFramebufferSize;
|
||||
//ovrTexture _eyeTextures[2]; // FIXME - not currently in use
|
||||
mutable int _hmdScreen { -1 };
|
||||
bool _hswDismissed { false };
|
||||
|
|
|
@ -25,51 +25,15 @@
|
|||
#include "OpenVrHelpers.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||
Q_LOGGING_CATEGORY(displayplugins, "hifi.displayplugins")
|
||||
|
||||
const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)");
|
||||
|
||||
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here
|
||||
|
||||
static vr::IVRCompositor* _compositor{ nullptr };
|
||||
static vr::TrackedDevicePose_t _presentThreadTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
|
||||
static mat4 _sensorResetMat;
|
||||
static uvec2 _windowSize;
|
||||
static uvec2 _renderTargetSize;
|
||||
|
||||
|
||||
|
||||
struct PerEyeData {
|
||||
//uvec2 _viewportOrigin;
|
||||
//uvec2 _viewportSize;
|
||||
mat4 _projectionMatrix;
|
||||
mat4 _eyeOffset;
|
||||
mat4 _pose;
|
||||
};
|
||||
|
||||
static PerEyeData _eyesData[2];
|
||||
|
||||
|
||||
template<typename F>
|
||||
void openvr_for_each_eye(F f) {
|
||||
f(vr::Hmd_Eye::Eye_Left);
|
||||
f(vr::Hmd_Eye::Eye_Right);
|
||||
}
|
||||
|
||||
mat4 toGlm(const vr::HmdMatrix44_t& m) {
|
||||
return glm::transpose(glm::make_mat4(&m.m[0][0]));
|
||||
}
|
||||
|
||||
mat4 toGlm(const vr::HmdMatrix34_t& m) {
|
||||
mat4 result = mat4(
|
||||
m.m[0][0], m.m[1][0], m.m[2][0], 0.0,
|
||||
m.m[0][1], m.m[1][1], m.m[2][1], 0.0,
|
||||
m.m[0][2], m.m[1][2], m.m[2][2], 0.0,
|
||||
m.m[0][3], m.m[1][3], m.m[2][3], 1.0f);
|
||||
return result;
|
||||
}
|
||||
static std::array<vr::Hmd_Eye, 2> VR_EYES { { vr::Eye_Left, vr::Eye_Right } };
|
||||
|
||||
bool OpenVrDisplayPlugin::isSupported() const {
|
||||
return vr::VR_IsHmdPresent();
|
||||
|
@ -78,12 +42,12 @@ bool OpenVrDisplayPlugin::isSupported() const {
|
|||
void OpenVrDisplayPlugin::activate() {
|
||||
_container->setIsOptionChecked(StandingHMDSensorMode, true);
|
||||
|
||||
if (!_hmd) {
|
||||
_hmd = acquireOpenVrSystem();
|
||||
if (!_system) {
|
||||
_system = acquireOpenVrSystem();
|
||||
}
|
||||
Q_ASSERT(_hmd);
|
||||
Q_ASSERT(_system);
|
||||
|
||||
_hmd->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
|
||||
_system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
|
||||
// Recommended render target size is per-eye, so double the X size for
|
||||
// left + right eyes
|
||||
_renderTargetSize.x *= 2;
|
||||
|
@ -91,52 +55,38 @@ void OpenVrDisplayPlugin::activate() {
|
|||
{
|
||||
Lock lock(_poseMutex);
|
||||
openvr_for_each_eye([&](vr::Hmd_Eye eye) {
|
||||
PerEyeData& eyeData = _eyesData[eye];
|
||||
eyeData._projectionMatrix = toGlm(_hmd->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL));
|
||||
eyeData._eyeOffset = toGlm(_hmd->GetEyeToHeadTransform(eye));
|
||||
_eyeOffsets[eye] = toGlm(_system->GetEyeToHeadTransform(eye));
|
||||
_eyeProjections[eye] = toGlm(_system->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL));
|
||||
});
|
||||
// FIXME Calculate the proper combined projection by using GetProjectionRaw values from both eyes
|
||||
_cullingProjection = _eyeProjections[0];
|
||||
|
||||
}
|
||||
|
||||
_compositor = vr::VRCompositor();
|
||||
Q_ASSERT(_compositor);
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
HmdDisplayPlugin::activate();
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::deactivate() {
|
||||
_container->setIsOptionChecked(StandingHMDSensorMode, false);
|
||||
if (_hmd) {
|
||||
if (_system) {
|
||||
releaseOpenVrSystem();
|
||||
_hmd = nullptr;
|
||||
_system = nullptr;
|
||||
}
|
||||
_compositor = nullptr;
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
HmdDisplayPlugin::deactivate();
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::customizeContext() {
|
||||
// Display plugins in DLLs must initialize glew locally
|
||||
static std::once_flag once;
|
||||
std::call_once(once, []{
|
||||
glewExperimental = true;
|
||||
GLenum err = glewInit();
|
||||
glGetError();
|
||||
});
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
|
||||
enableVsync(false);
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
_enablePreview = !isVsyncEnabled();
|
||||
}
|
||||
|
||||
uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
|
||||
return _renderTargetSize;
|
||||
}
|
||||
|
||||
mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const {
|
||||
// FIXME hack to ensure that we don't crash trying to get the combined matrix
|
||||
if (eye == Mono) {
|
||||
eye = Left;
|
||||
}
|
||||
Lock lock(_poseMutex);
|
||||
return _eyesData[eye]._projectionMatrix;
|
||||
HmdDisplayPlugin::customizeContext();
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::resetSensors() {
|
||||
|
@ -145,65 +95,47 @@ void OpenVrDisplayPlugin::resetSensors() {
|
|||
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(m));
|
||||
}
|
||||
|
||||
glm::mat4 OpenVrDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
|
||||
Lock lock(_poseMutex);
|
||||
return _eyesData[eye]._eyeOffset;
|
||||
}
|
||||
|
||||
glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
||||
Lock lock(_poseMutex);
|
||||
|
||||
float displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
|
||||
float frameDuration = 1.f / displayFrequency;
|
||||
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.
|
||||
const float NUM_PREDICTION_FRAMES = 3.0f;
|
||||
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];
|
||||
_system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, predictedSecondsFromNow, predictedTrackedDevicePose, vr::k_unMaxTrackedDeviceCount);
|
||||
|
||||
// copy and process predictedTrackedDevicePoses
|
||||
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
|
||||
_trackedDevicePose[i] = predictedTrackedDevicePose[i];
|
||||
_trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking);
|
||||
}
|
||||
return _trackedDevicePoseMat4[0];
|
||||
}
|
||||
|
||||
|
||||
void OpenVrDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize);
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::internalPresent() {
|
||||
// Flip y-axis since GL UV coords are backwards.
|
||||
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
||||
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
||||
|
||||
// screen preview mirroring
|
||||
if (_enablePreview) {
|
||||
auto windowSize = toGlm(_window->size());
|
||||
if (_monoPreview) {
|
||||
glViewport(0, 0, windowSize.x * 2, windowSize.y);
|
||||
glScissor(0, windowSize.y, windowSize.x, windowSize.y);
|
||||
} else {
|
||||
glViewport(0, 0, windowSize.x, windowSize.y);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
GLenum err = glGetError();
|
||||
Q_ASSERT(0 == err);
|
||||
drawUnitQuad();
|
||||
}
|
||||
|
||||
vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
|
||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||
|
||||
glFinish();
|
||||
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
||||
if (_enablePreview) {
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
_compositor->WaitGetPoses(_presentThreadTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
||||
{
|
||||
// copy and process _presentThreadTrackedDevicePoses
|
||||
Lock lock(_poseMutex);
|
||||
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
|
||||
_trackedDevicePose[i] = _presentThreadTrackedDevicePose[i];
|
||||
_trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking);
|
||||
}
|
||||
openvr_for_each_eye([&](vr::Hmd_Eye eye) {
|
||||
_eyesData[eye]._pose = _trackedDevicePoseMat4[0];
|
||||
});
|
||||
}
|
||||
|
||||
//WindowOpenGLDisplayPlugin::internalPresent();
|
||||
// Handle the mirroring in the base class
|
||||
HmdDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
|
|
@ -11,15 +11,14 @@
|
|||
|
||||
#include <openvr.h>
|
||||
|
||||
#include <display-plugins/WindowOpenGLDisplayPlugin.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
|
||||
|
||||
class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
class OpenVrDisplayPlugin : public HmdDisplayPlugin {
|
||||
public:
|
||||
virtual bool isSupported() const override;
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual bool isHmd() const override { return true; }
|
||||
|
||||
virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; }
|
||||
|
||||
|
@ -28,25 +27,16 @@ public:
|
|||
|
||||
virtual void customizeContext() override;
|
||||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
|
||||
|
||||
// Stereo specific methods
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
virtual void resetSensors() override;
|
||||
|
||||
virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override;
|
||||
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
|
||||
protected:
|
||||
virtual void internalPresent() override;
|
||||
|
||||
private:
|
||||
vr::IVRSystem* _hmd { nullptr };
|
||||
vr::IVRSystem* _system { nullptr };
|
||||
static const QString NAME;
|
||||
bool _enablePreview { false };
|
||||
bool _monoPreview { true };
|
||||
mutable Mutex _poseMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||
Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
|
||||
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
|
|
@ -15,3 +15,21 @@
|
|||
vr::IVRSystem* acquireOpenVrSystem();
|
||||
void releaseOpenVrSystem();
|
||||
|
||||
template<typename F>
|
||||
void openvr_for_each_eye(F f) {
|
||||
f(vr::Hmd_Eye::Eye_Left);
|
||||
f(vr::Hmd_Eye::Eye_Right);
|
||||
}
|
||||
|
||||
inline mat4 toGlm(const vr::HmdMatrix44_t& m) {
|
||||
return glm::transpose(glm::make_mat4(&m.m[0][0]));
|
||||
}
|
||||
|
||||
inline mat4 toGlm(const vr::HmdMatrix34_t& m) {
|
||||
mat4 result = mat4(
|
||||
m.m[0][0], m.m[1][0], m.m[2][0], 0.0,
|
||||
m.m[0][1], m.m[1][1], m.m[2][1], 0.0,
|
||||
m.m[0][2], m.m[1][2], m.m[2][2], 0.0,
|
||||
m.m[0][3], m.m[1][3], m.m[2][3], 1.0f);
|
||||
return result;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue