mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'master' into 20812
This commit is contained in:
commit
881302df02
26 changed files with 1039 additions and 155 deletions
|
@ -55,13 +55,7 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
{
|
||||
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
|
||||
QThread* assetThread = new QThread;
|
||||
assetThread->setObjectName("Asset Thread");
|
||||
assetClient->moveToThread(assetThread);
|
||||
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
assetThread->start();
|
||||
ResourceManager::init();
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
|
@ -471,11 +465,7 @@ void Agent::aboutToFinish() {
|
|||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
// cleanup the AssetClient thread
|
||||
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
|
||||
DependencyManager::destroy<AssetClient>();
|
||||
assetThread->quit();
|
||||
assetThread->wait();
|
||||
ResourceManager::cleanup();
|
||||
|
||||
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
|
115
examples/away.js
115
examples/away.js
|
@ -13,13 +13,30 @@
|
|||
//
|
||||
// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key.
|
||||
// See MAIN CONTROL, below, for what "paused" actually does.
|
||||
var OVERLAY_RATIO = 1920 / 1080;
|
||||
var OVERLAY_WIDTH = 1920;
|
||||
var OVERLAY_HEIGHT = 1080;
|
||||
var OVERLAY_RATIO = OVERLAY_WIDTH / OVERLAY_HEIGHT;
|
||||
var OVERLAY_DATA = {
|
||||
width: OVERLAY_WIDTH,
|
||||
height: OVERLAY_HEIGHT,
|
||||
imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
};
|
||||
|
||||
var lastOverlayPosition = { x: 0, y: 0, z: 0};
|
||||
var OVERLAY_DATA_HMD = {
|
||||
position: lastOverlayPosition,
|
||||
width: OVERLAY_WIDTH,
|
||||
height: OVERLAY_HEIGHT,
|
||||
url: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
scale: 2,
|
||||
isFacingAvatar: true,
|
||||
drawInFront: true
|
||||
};
|
||||
|
||||
// ANIMATION
|
||||
// We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect
|
||||
// using an animation graph with a state that we turn on and off through the animation var defined with that state.
|
||||
|
@ -64,29 +81,74 @@ function stopAwayAnimation() {
|
|||
|
||||
// OVERLAY
|
||||
var overlay = Overlays.addOverlay("image", OVERLAY_DATA);
|
||||
var overlayHMD = Overlays.addOverlay("image3d", OVERLAY_DATA_HMD);
|
||||
|
||||
function moveCloserToCamera(positionAtHUD) {
|
||||
// we don't actually want to render at the slerped look at... instead, we want to render
|
||||
// slightly closer to the camera than that.
|
||||
var MOVE_CLOSER_TO_CAMERA_BY = -0.25;
|
||||
var cameraFront = Quat.getFront(Camera.orientation);
|
||||
var closerToCamera = Vec3.multiply(cameraFront, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera
|
||||
var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera);
|
||||
|
||||
return slightlyCloserPosition;
|
||||
}
|
||||
|
||||
function showOverlay() {
|
||||
var properties = {visible: true},
|
||||
// Update for current screen size, keeping overlay proportions constant.
|
||||
screen = Controller.getViewportDimensions(),
|
||||
screenRatio = screen.x / screen.y;
|
||||
if (screenRatio < OVERLAY_RATIO) {
|
||||
properties.width = screen.x;
|
||||
properties.height = screen.x / OVERLAY_RATIO;
|
||||
properties.x = 0;
|
||||
properties.y = (screen.y - properties.height) / 2;
|
||||
var properties = {visible: true};
|
||||
|
||||
if (HMD.active) {
|
||||
// make sure desktop version is hidden
|
||||
Overlays.editOverlay(overlay, { visible: false });
|
||||
|
||||
lastOverlayPosition = HMD.getHUDLookAtPosition3D();
|
||||
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
|
||||
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
|
||||
} else {
|
||||
properties.height = screen.y;
|
||||
properties.width = screen.y * OVERLAY_RATIO;
|
||||
properties.y = 0;
|
||||
properties.x = (screen.x - properties.width) / 2;
|
||||
// make sure HMD is hidden
|
||||
Overlays.editOverlay(overlayHMD, { visible: false });
|
||||
|
||||
// Update for current screen size, keeping overlay proportions constant.
|
||||
var screen = Controller.getViewportDimensions();
|
||||
|
||||
// keep the overlay it's natural size and always center it...
|
||||
Overlays.editOverlay(overlay, { visible: true,
|
||||
x: ((screen.x - OVERLAY_WIDTH) / 2),
|
||||
y: ((screen.y - OVERLAY_HEIGHT) / 2) });
|
||||
}
|
||||
Overlays.editOverlay(overlay, properties);
|
||||
}
|
||||
function hideOverlay() {
|
||||
Overlays.editOverlay(overlay, {visible: false});
|
||||
Overlays.editOverlay(overlayHMD, {visible: false});
|
||||
}
|
||||
hideOverlay();
|
||||
|
||||
function maybeMoveOverlay() {
|
||||
if (isAway) {
|
||||
// if we switched from HMD to Desktop, make sure to hide our HUD overlay and show the
|
||||
// desktop overlay
|
||||
if (!HMD.active) {
|
||||
showOverlay(); // this will also recenter appropriately
|
||||
}
|
||||
|
||||
if (HMD.active) {
|
||||
// Note: instead of moving it directly to the lookAt, we will move it slightly toward the
|
||||
// new look at. This will result in a more subtle slerp toward the look at and reduce jerkiness
|
||||
var EASE_BY_RATIO = 0.1;
|
||||
var lookAt = HMD.getHUDLookAtPosition3D();
|
||||
var lookAtChange = Vec3.subtract(lookAt, lastOverlayPosition);
|
||||
var halfWayBetweenOldAndLookAt = Vec3.multiply(lookAtChange, EASE_BY_RATIO);
|
||||
var newOverlayPosition = Vec3.sum(lastOverlayPosition, halfWayBetweenOldAndLookAt);
|
||||
lastOverlayPosition = newOverlayPosition;
|
||||
|
||||
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
|
||||
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
|
||||
|
||||
// make sure desktop version is hidden
|
||||
Overlays.editOverlay(overlay, { visible: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MAIN CONTROL
|
||||
var wasMuted, isAway;
|
||||
|
@ -106,6 +168,12 @@ function goAway() {
|
|||
MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view
|
||||
playAwayAnimation(); // animation is still seen by others
|
||||
showOverlay();
|
||||
|
||||
// tell the Reticle, we want to stop capturing the mouse until we come back
|
||||
Reticle.allowMouseCapture = false;
|
||||
if (HMD.active) {
|
||||
Reticle.visible = false;
|
||||
}
|
||||
}
|
||||
function goActive() {
|
||||
if (!isAway) {
|
||||
|
@ -119,13 +187,20 @@ function goActive() {
|
|||
MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.
|
||||
stopAwayAnimation();
|
||||
hideOverlay();
|
||||
|
||||
// tell the Reticle, we are ready to capture the mouse again and it should be visible
|
||||
Reticle.allowMouseCapture = true;
|
||||
Reticle.visible = true;
|
||||
if (HMD.active) {
|
||||
Reticle.position = HMD.getHUDLookAtPosition2D();
|
||||
}
|
||||
}
|
||||
|
||||
function maybeGoActive(event) {
|
||||
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
|
||||
return;
|
||||
}
|
||||
if (!isAway && (event.text === '.')) {
|
||||
if (!isAway && (event.text == 'ESC')) {
|
||||
goAway();
|
||||
} else {
|
||||
goActive();
|
||||
|
@ -141,10 +216,8 @@ function maybeGoAway() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD, but
|
||||
// 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) {
|
||||
|
@ -153,6 +226,8 @@ function maybeGoAway() {
|
|||
}
|
||||
}
|
||||
|
||||
Script.update.connect(maybeMoveOverlay);
|
||||
|
||||
Script.update.connect(maybeGoAway);
|
||||
Controller.mousePressEvent.connect(goActive);
|
||||
Controller.keyPressEvent.connect(maybeGoActive);
|
||||
|
|
|
@ -22,3 +22,4 @@ Script.load("grab.js");
|
|||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
Script.load("attachedEntitiesManager.js");
|
||||
Script.load("depthReticle.js");
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// When used in HMD, this script will make the reticle depth track to any clickable item in view.
|
||||
// This script also handles auto-hiding the reticle after inactivity, as well as having the reticle
|
||||
// seek the look at position upon waking up.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -17,8 +19,89 @@ var desiredDepth = APPARENT_2D_OVERLAY_DEPTH;
|
|||
var TIME_BETWEEN_DEPTH_CHECKS = 100;
|
||||
var MINIMUM_DEPTH_ADJUST = 0.01;
|
||||
var NON_LINEAR_DIVISOR = 2;
|
||||
var MINIMUM_SEEK_DISTANCE = 0.01;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
var lastMouseMove = Date.now();
|
||||
var lastMouseX = Reticle.position.x;
|
||||
var lastMouseY = Reticle.position.y;
|
||||
var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds
|
||||
var shouldSeekToLookAt = false;
|
||||
var fastMouseMoves = 0;
|
||||
var averageMouseVelocity = 0;
|
||||
var WEIGHTING = 1/20; // simple moving average over last 20 samples
|
||||
var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
|
||||
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
|
||||
|
||||
Controller.mouseMoveEvent.connect(function(mouseEvent) {
|
||||
var now = Date.now();
|
||||
|
||||
// if the reticle is hidden, and we're not in away mode...
|
||||
if (!Reticle.visible && Reticle.allowMouseCapture) {
|
||||
Reticle.visible = true;
|
||||
if (HMD.active) {
|
||||
shouldSeekToLookAt = true;
|
||||
}
|
||||
} else {
|
||||
// even if the reticle is visible, if we're in HMD mode, and the person is moving their mouse quickly (shaking it)
|
||||
// then they are probably looking for it, and we should move into seekToLookAt mode
|
||||
if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
|
||||
var dx = Reticle.position.x - lastMouseX;
|
||||
var dy = Reticle.position.y - lastMouseY;
|
||||
var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move
|
||||
var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy));
|
||||
var mouseVelocity = mouseMoveDistance / dt;
|
||||
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity);
|
||||
if (averageMouseVelocity > AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO) {
|
||||
shouldSeekToLookAt = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastMouseMove = now;
|
||||
lastMouseX = mouseEvent.x;
|
||||
lastMouseY = mouseEvent.y;
|
||||
});
|
||||
|
||||
function seekToLookAt() {
|
||||
// if we're currently seeking the lookAt move the mouse toward the lookat
|
||||
if (shouldSeekToLookAt) {
|
||||
averageMouseVelocity = 0; // reset this, these never count for movement...
|
||||
var lookAt2D = HMD.getHUDLookAtPosition2D();
|
||||
var currentReticlePosition = Reticle.position;
|
||||
var distanceBetweenX = lookAt2D.x - Reticle.position.x;
|
||||
var distanceBetweenY = lookAt2D.y - Reticle.position.y;
|
||||
var moveX = distanceBetweenX / NON_LINEAR_DIVISOR;
|
||||
var moveY = distanceBetweenY / NON_LINEAR_DIVISOR;
|
||||
var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY };
|
||||
var closeEnoughX = false;
|
||||
var closeEnoughY = false;
|
||||
if (moveX < MINIMUM_SEEK_DISTANCE) {
|
||||
newPosition.x = lookAt2D.x;
|
||||
closeEnoughX = true;
|
||||
}
|
||||
if (moveY < MINIMUM_SEEK_DISTANCE) {
|
||||
newPosition.y = lookAt2D.y;
|
||||
closeEnoughY = true;
|
||||
}
|
||||
Reticle.position = newPosition;
|
||||
if (closeEnoughX && closeEnoughY) {
|
||||
shouldSeekToLookAt = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function autoHideReticle() {
|
||||
// if we haven't moved in a long period of time, and we're not pointing at some
|
||||
// system overlay (like a window), then hide the reticle
|
||||
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
||||
var now = Date.now();
|
||||
var timeSinceLastMouseMove = now - lastMouseMove;
|
||||
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
||||
Reticle.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkReticleDepth() {
|
||||
var now = Date.now();
|
||||
var timeSinceLastDepthCheck = now - lastDepthCheckTime;
|
||||
if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) {
|
||||
|
@ -56,6 +139,9 @@ Script.update.connect(function(deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function moveToDesiredDepth() {
|
||||
// move the reticle toward the desired depth
|
||||
if (desiredDepth != Reticle.depth) {
|
||||
|
||||
|
@ -69,4 +155,13 @@ Script.update.connect(function(deltaTime) {
|
|||
|
||||
Reticle.setDepth(newDepth);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
autoHideReticle(); // auto hide reticle for desktop or HMD mode
|
||||
if (HMD.active) {
|
||||
seekToLookAt(); // handle moving the reticle toward the look at
|
||||
checkReticleDepth(); // make sure reticle is at correct depth
|
||||
moveToDesiredDepth(); // move the fade the reticle to the desired depth
|
||||
}
|
||||
});
|
||||
|
|
109
examples/homeContent/whiteboardV2/eraserEntityScript.js
Normal file
109
examples/homeContent/whiteboardV2/eraserEntityScript.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// eraserEntityScript.js
|
||||
// examples/homeContent/eraserEntityScript
|
||||
//
|
||||
// Created by Eric Levin on 2/17/15.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script provides logic for an object with attached script to erase nearby marker strokes
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
var _this;
|
||||
Eraser = function() {
|
||||
_this = this;
|
||||
|
||||
_this.ERASER_TRIGGER_THRESHOLD = 0.2;
|
||||
_this.STROKE_NAME = "hifi-marker-stroke";
|
||||
_this.ERASER_TO_STROKE_SEARCH_RADIUS = 0.7;
|
||||
_this.ERASER_RESET_WAIT_TIME = 3000;
|
||||
};
|
||||
|
||||
Eraser.prototype = {
|
||||
|
||||
startEquip: function(id, params) {
|
||||
_this.equipped = true;
|
||||
_this.hand = params[0] == "left" ? 0 : 1;
|
||||
// We really only need to grab position of marker strokes once, and then just check to see if eraser comes near enough to those strokes
|
||||
Overlays.editOverlay(_this.searchSphere, {
|
||||
visible: true
|
||||
});
|
||||
},
|
||||
continueEquip: function() {
|
||||
_this.eraserPosition = Entities.getEntityProperties(_this.entityID, "position").position;
|
||||
Overlays.editOverlay(_this.searchSphere, {
|
||||
position: _this.eraserPosition
|
||||
});
|
||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[_this.hand]);
|
||||
if (_this.triggerValue > _this.ERASER_TRIGGER_THRESHOLD) {
|
||||
_this.continueHolding();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
continueHolding: function() {
|
||||
var strokeIDs = Entities.findEntities(_this.eraserPosition, _this.ERASER_TO_STROKE_SEARCH_RADIUS);
|
||||
// Create a map of stroke entities and their positions
|
||||
|
||||
strokeIDs.forEach(function(strokeID) {
|
||||
var strokeProps = Entities.getEntityProperties(strokeID, ["position", "name"]);
|
||||
if (strokeProps.name === _this.STROKE_NAME && Vec3.distance(_this.eraserPosition, strokeProps.position) < _this.ERASER_TO_STROKE_SEARCH_RADIUS) {
|
||||
Entities.deleteEntity(strokeID);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
releaseEquip: function() {
|
||||
Overlays.editOverlay(_this.searchSphere, {
|
||||
visible: false
|
||||
});
|
||||
|
||||
// Once user releases eraser, wait a bit then put marker back to its original position and rotation
|
||||
Script.setTimeout(function() {
|
||||
var userData = getEntityUserData(_this.entityID);
|
||||
Entities.editEntity(_this.entityID, {
|
||||
position: userData.originalPosition,
|
||||
rotation: userData.originalRotation,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
}, _this.ERASER_RESET_WAIT_TIME);
|
||||
},
|
||||
|
||||
|
||||
|
||||
preload: function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
_this.searchSphere = Overlays.addOverlay('sphere', {
|
||||
size: _this.ERASER_TO_STROKE_SEARCH_RADIUS,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 10
|
||||
},
|
||||
alpha: 0.2,
|
||||
solid: true,
|
||||
visible: false
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
Overlays.deleteOverlay(_this.searchSphere);
|
||||
}
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Eraser();
|
||||
});
|
219
examples/homeContent/whiteboardV2/markerEntityScript.js
Normal file
219
examples/homeContent/whiteboardV2/markerEntityScript.js
Normal file
|
@ -0,0 +1,219 @@
|
|||
//
|
||||
// markerTipEntityScript.js
|
||||
// examples/homeContent/markerTipEntityScript
|
||||
//
|
||||
// Created by Eric Levin on 2/17/15.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This script provides the logic for an object to draw marker strokes on its associated whiteboard
|
||||
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
var MAX_POINTS_PER_STROKE = 40;
|
||||
var _this;
|
||||
MarkerTip = function() {
|
||||
_this = this;
|
||||
_this.MARKER_TEXTURE_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/textures/markerStroke.png";
|
||||
_this.strokeForwardOffset = 0.0001;
|
||||
_this.STROKE_WIDTH_RANGE = {
|
||||
min: 0.002,
|
||||
max: 0.01
|
||||
};
|
||||
_this.MAX_MARKER_TO_BOARD_DISTANCE = 1.4;
|
||||
_this.MIN_DISTANCE_BETWEEN_POINTS = 0.002;
|
||||
_this.MAX_DISTANCE_BETWEEN_POINTS = 0.1;
|
||||
_this.strokes = [];
|
||||
_this.PAINTING_TRIGGER_THRESHOLD = 0.2;
|
||||
_this.STROKE_NAME = "hifi-marker-stroke";
|
||||
_this.WHITEBOARD_SURFACE_NAME = "hifi-whiteboardDrawingSurface";
|
||||
_this.MARKER_RESET_WAIT_TIME = 3000;
|
||||
};
|
||||
|
||||
MarkerTip.prototype = {
|
||||
|
||||
startEquip: function(id, params) {
|
||||
_this.whiteboards = [];
|
||||
_this.equipped = true;
|
||||
_this.hand = params[0] == "left" ? 0 : 1;
|
||||
_this.markerColor = getEntityUserData(_this.entityID).markerColor;
|
||||
// search for whiteboards
|
||||
var markerPosition = Entities.getEntityProperties(_this.entityID, "position").position;
|
||||
var entities = Entities.findEntities(markerPosition, 10);
|
||||
entities.forEach(function(entity) {
|
||||
var entityName = Entities.getEntityProperties(entity, "name").name;
|
||||
if (entityName === _this.WHITEBOARD_SURFACE_NAME) {
|
||||
|
||||
_this.whiteboards.push(entity);
|
||||
}
|
||||
});
|
||||
|
||||
print("intersectable entities " + JSON.stringify(_this.whiteboards))
|
||||
},
|
||||
|
||||
releaseEquip: function() {
|
||||
_this.resetStroke();
|
||||
Overlays.editOverlay(_this.laserPointer, {
|
||||
visible: false
|
||||
});
|
||||
|
||||
// Once user releases marker, wait a bit then put marker back to its original position and rotation
|
||||
Script.setTimeout(function() {
|
||||
var userData = getEntityUserData(_this.entityID);
|
||||
Entities.editEntity(_this.entityID, {
|
||||
position: userData.originalPosition,
|
||||
rotation: userData.originalRotation,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
}, _this.MARKER_RESET_WAIT_TIME);
|
||||
},
|
||||
|
||||
|
||||
continueEquip: function() {
|
||||
// cast a ray from marker and see if it hits anything
|
||||
var markerProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation"]);
|
||||
|
||||
var pickRay = {
|
||||
origin: markerProps.position,
|
||||
direction: Quat.getFront(markerProps.rotation)
|
||||
}
|
||||
var intersection = Entities.findRayIntersectionBlocking(pickRay, true, _this.whiteboards);
|
||||
|
||||
if (intersection.intersects && Vec3.distance(intersection.intersection, markerProps.position) < _this.MAX_MARKER_TO_BOARD_DISTANCE) {
|
||||
_this.currentWhiteboard = intersection.entityID;
|
||||
var whiteboardRotation = Entities.getEntityProperties(_this.currentWhiteboard, "rotation").rotation;
|
||||
_this.whiteboardNormal = Quat.getFront(whiteboardRotation);
|
||||
Overlays.editOverlay(_this.laserPointer, {
|
||||
visible: true,
|
||||
position: intersection.intersection,
|
||||
rotation: whiteboardRotation
|
||||
})
|
||||
_this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[_this.hand]);
|
||||
if (_this.triggerValue > _this.PAINTING_TRIGGER_THRESHOLD) {
|
||||
_this.paint(intersection.intersection)
|
||||
} else {
|
||||
_this.resetStroke();
|
||||
}
|
||||
} else {
|
||||
if (_this.currentStroke) {
|
||||
_this.resetStroke();
|
||||
}
|
||||
|
||||
Overlays.editOverlay(_this.laserPointer, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
newStroke: function(position) {
|
||||
_this.strokeBasePosition = position;
|
||||
_this.currentStroke = Entities.addEntity({
|
||||
type: "PolyLine",
|
||||
name: _this.STROKE_NAME,
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
position: position,
|
||||
textures: _this.MARKER_TEXTURE_URL,
|
||||
color: _this.markerColor,
|
||||
lifetime: 5000,
|
||||
});
|
||||
|
||||
_this.linePoints = [];
|
||||
_this.normals = [];
|
||||
_this.strokes.push(_this.currentStroke);
|
||||
},
|
||||
|
||||
paint: function(position) {
|
||||
var basePosition = position;
|
||||
if (!_this.currentStroke) {
|
||||
if (_this.oldPosition) {
|
||||
basePosition = _this.oldPosition;
|
||||
}
|
||||
_this.newStroke(basePosition);
|
||||
}
|
||||
|
||||
var localPoint = Vec3.subtract(basePosition, _this.strokeBasePosition);
|
||||
localPoint = Vec3.sum(localPoint, Vec3.multiply(_this.whiteboardNormal, _this.strokeForwardOffset));
|
||||
|
||||
if (_this.linePoints.length > 0) {
|
||||
var distance = Vec3.distance(localPoint, _this.linePoints[_this.linePoints.length - 1]);
|
||||
if (distance < _this.MIN_DISTANCE_BETWEEN_POINTS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_this.linePoints.push(localPoint);
|
||||
_this.normals.push(_this.whiteboardNormal);
|
||||
|
||||
var strokeWidths = [];
|
||||
for (var i = 0; i < _this.linePoints.length; i++) {
|
||||
// Create a temp array of stroke widths for calligraphy effect - start and end should be less wide
|
||||
var pointsFromCenter = Math.abs(_this.linePoints.length / 2 - i);
|
||||
var pointWidth = map(pointsFromCenter, 0, this.linePoints.length / 2, _this.STROKE_WIDTH_RANGE.max, this.STROKE_WIDTH_RANGE.min);
|
||||
strokeWidths.push(pointWidth);
|
||||
}
|
||||
|
||||
Entities.editEntity(_this.currentStroke, {
|
||||
linePoints: _this.linePoints,
|
||||
normals: _this.normals,
|
||||
strokeWidths: strokeWidths
|
||||
});
|
||||
|
||||
if (_this.linePoints.length > MAX_POINTS_PER_STROKE) {
|
||||
Entities.editEntity(_this.currentStroke, {
|
||||
parentID: _this.currentWhiteboard
|
||||
});
|
||||
_this.currentStroke = null;
|
||||
_this.oldPosition = position;
|
||||
}
|
||||
},
|
||||
resetStroke: function() {
|
||||
|
||||
Entities.editEntity(_this.currentStroke, {
|
||||
parentID: _this.currentWhiteboard
|
||||
});
|
||||
_this.currentStroke = null;
|
||||
|
||||
_this.oldPosition = null;
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
_this.laserPointer = Overlays.addOverlay("circle3d", {
|
||||
color: {
|
||||
red: 220,
|
||||
green: 35,
|
||||
blue: 53
|
||||
},
|
||||
solid: true,
|
||||
size: 0.01,
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
Overlays.deleteOverlay(_this.laserPointer);
|
||||
_this.strokes.forEach(function(stroke) {
|
||||
Entities.deleteEntity(stroke);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new MarkerTip();
|
||||
});
|
254
examples/homeContent/whiteboardV2/whiteboardSpawner.js
Normal file
254
examples/homeContent/whiteboardV2/whiteboardSpawner.js
Normal file
|
@ -0,0 +1,254 @@
|
|||
//
|
||||
// whiteboardSpawner.js
|
||||
// examples/homeContent/whiteboardV2
|
||||
//
|
||||
// Created by Eric Levina on 2/17/16
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Run this script to spawn a whiteboard, markers, and an eraser.
|
||||
// To draw on the whiteboard, equip a marker and hold down trigger with marker tip pointed at whiteboard
|
||||
//
|
||||
// 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 orientation = MyAvatar.orientation;
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
var markerRotation = Quat.fromVec3Degrees({
|
||||
x: orientation.x + 10,
|
||||
y: orientation.y - 90,
|
||||
z: orientation.z
|
||||
})
|
||||
orientation.x = 0;
|
||||
var whiteboardRotation = Quat.fromVec3Degrees({
|
||||
x: 0,
|
||||
y: orientation.y,
|
||||
z: 0
|
||||
});
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var markers = [];
|
||||
|
||||
|
||||
var whiteboardPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(orientation)));
|
||||
var WHITEBOARD_MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/Whiteboard-4.fbx";
|
||||
var WHITEBOARD_COLLISION_HULL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/whiteboardCollisionHull.obj";
|
||||
var whiteboard = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "whiteboard",
|
||||
modelURL: WHITEBOARD_MODEL_URL,
|
||||
position: whiteboardPosition,
|
||||
rotation: whiteboardRotation,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: WHITEBOARD_COLLISION_HULL_URL,
|
||||
dimensions: {
|
||||
x: 1.86,
|
||||
y: 2.7,
|
||||
z: 0.4636
|
||||
},
|
||||
});
|
||||
|
||||
var whiteboardSurfacePosition = Vec3.sum(whiteboardPosition, {
|
||||
x: 0.0,
|
||||
y: 0.45,
|
||||
z: 0.0
|
||||
});
|
||||
whiteboardSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(-0.02, Quat.getRight(whiteboardRotation)));
|
||||
var moveForwardDistance = 0.02;
|
||||
whiteboardFrontSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(-moveForwardDistance, Quat.getFront(whiteboardRotation)));
|
||||
var whiteboardSurfaceSettings = {
|
||||
type: "Box",
|
||||
name: "hifi-whiteboardDrawingSurface",
|
||||
dimensions: {
|
||||
x: 1.82,
|
||||
y: 1.8,
|
||||
z: 0.01
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 200
|
||||
},
|
||||
position: whiteboardFrontSurfacePosition,
|
||||
rotation: whiteboardRotation,
|
||||
visible: false,
|
||||
parentID: whiteboard
|
||||
}
|
||||
var whiteboardFrontDrawingSurface = Entities.addEntity(whiteboardSurfaceSettings);
|
||||
|
||||
|
||||
whiteboardBackSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(moveForwardDistance, Quat.getFront(whiteboardRotation)));
|
||||
whiteboardSurfaceSettings.position = whiteboardBackSurfacePosition;
|
||||
|
||||
var whiteboardBackDrawingSurface = Entities.addEntity(whiteboardSurfaceSettings);
|
||||
|
||||
|
||||
var WHITEBOARD_RACK_DEPTH = 1.9;
|
||||
|
||||
var ERASER_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/eraser-2.fbx";
|
||||
var ERASER_SCRIPT_URL = Script.resolvePath("eraserEntityScript.js?v43");
|
||||
var eraserPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(WHITEBOARD_RACK_DEPTH, Quat.getFront(whiteboardRotation)));
|
||||
eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.5, Quat.getRight(whiteboardRotation)));
|
||||
var eraserRotation = markerRotation;
|
||||
|
||||
var eraser = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: ERASER_MODEL_URL,
|
||||
position: eraserPosition,
|
||||
script: ERASER_SCRIPT_URL,
|
||||
shapeType: "box",
|
||||
dimensions: {
|
||||
x: 0.0858,
|
||||
y: 0.0393,
|
||||
z: 0.2083
|
||||
},
|
||||
rotation: eraserRotation,
|
||||
dynamic: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -1,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
},
|
||||
userData: JSON.stringify({
|
||||
originalPosition: eraserPosition,
|
||||
originalRotation: eraserRotation,
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.020,
|
||||
y: 0.120,
|
||||
z: 0.049
|
||||
}, {
|
||||
x: 0.1004,
|
||||
y: 0.6424,
|
||||
z: 0.717,
|
||||
w: 0.250
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: -0.005,
|
||||
y: 0.1101,
|
||||
z: 0.053
|
||||
}, {
|
||||
x: 0.723,
|
||||
y: 0.289,
|
||||
z: 0.142,
|
||||
w: 0.610
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
createMarkers();
|
||||
|
||||
function createMarkers() {
|
||||
var modelURLS = [
|
||||
"https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-blue.fbx",
|
||||
"https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-red.fbx",
|
||||
"https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-black.fbx",
|
||||
];
|
||||
|
||||
var markerPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(WHITEBOARD_RACK_DEPTH, Quat.getFront(orientation)));
|
||||
|
||||
createMarker(modelURLS[0], markerPosition, {
|
||||
red: 10,
|
||||
green: 10,
|
||||
blue: 200
|
||||
});
|
||||
|
||||
markerPosition = Vec3.sum(markerPosition, Vec3.multiply(-0.2, Quat.getFront(markerRotation)));
|
||||
createMarker(modelURLS[1], markerPosition, {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 10
|
||||
});
|
||||
|
||||
markerPosition = Vec3.sum(markerPosition, Vec3.multiply(0.4, Quat.getFront(markerRotation)));
|
||||
createMarker(modelURLS[2], markerPosition, {
|
||||
red: 10,
|
||||
green: 10,
|
||||
blue: 10
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createMarker(modelURL, markerPosition, markerColor) {
|
||||
var MARKER_SCRIPT_URL = Script.resolvePath("markerEntityScript.js?v1" + Math.random());
|
||||
var marker = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
rotation: markerRotation,
|
||||
shapeType: "box",
|
||||
name: "marker",
|
||||
dynamic: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -1,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
},
|
||||
position: markerPosition,
|
||||
dimensions: {
|
||||
x: 0.027,
|
||||
y: 0.027,
|
||||
z: 0.164
|
||||
},
|
||||
name: "marker",
|
||||
script: MARKER_SCRIPT_URL,
|
||||
userData: JSON.stringify({
|
||||
originalPosition: markerPosition,
|
||||
originalRotation: markerRotation,
|
||||
markerColor: markerColor,
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.001,
|
||||
y: 0.139,
|
||||
z: 0.050
|
||||
}, {
|
||||
x: -0.73,
|
||||
y: -0.043,
|
||||
z: -0.108,
|
||||
w: -0.666
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: 0.007,
|
||||
y: 0.151,
|
||||
z: 0.061
|
||||
}, {
|
||||
x: -0.417,
|
||||
y: 0.631,
|
||||
z: -0.389,
|
||||
w: -0.525
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
markers.push(marker);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(whiteboard);
|
||||
Entities.deleteEntity(whiteboardFrontDrawingSurface);
|
||||
Entities.deleteEntity(whiteboardBackDrawingSurface);
|
||||
Entities.deleteEntity(eraser);
|
||||
markers.forEach(function(marker) {
|
||||
Entities.deleteEntity(marker);
|
||||
});
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -133,13 +133,14 @@ TableView {
|
|||
HiFiGlyphs {
|
||||
id: reloadButton
|
||||
text: hifi.glyphs.reloadSmall
|
||||
color: parent.color
|
||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: stopButton.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MouseArea {
|
||||
id: reloadButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: reloadScript(model.url)
|
||||
}
|
||||
|
@ -149,13 +150,14 @@ TableView {
|
|||
HiFiGlyphs {
|
||||
id: stopButton
|
||||
text: hifi.glyphs.closeSmall
|
||||
color: parent.color
|
||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MouseArea {
|
||||
id: stopButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: stopScript(model.url)
|
||||
}
|
||||
|
|
|
@ -56,14 +56,23 @@ TreeView {
|
|||
|
||||
branchDelegate: HiFiGlyphs {
|
||||
text: styleData.isExpanded ? hifi.glyphs.disclosureCollapse : hifi.glyphs.disclosureExpand
|
||||
size: hifi.fontSizes.tableText * 2.5 // tableText is in points; proportionately scale to pixels
|
||||
size: hifi.fontSizes.tableText * 2.5
|
||||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
? (styleData.selected
|
||||
? hifi.colors.black
|
||||
: (iconArea.pressed ? hifi.colors.white : hifi.colors.baseGrayHighlight))
|
||||
: (styleData.selected
|
||||
? hifi.colors.black
|
||||
: (iconArea.pressed ? hifi.colors.white : hifi.colors.lightGrayText))
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding / 2
|
||||
}
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
|
||||
handle: Item {
|
||||
|
|
|
@ -370,7 +370,6 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<AutoUpdater>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
DependencyManager::set<InterfaceActionFactory>();
|
||||
DependencyManager::set<AssetClient>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
|
@ -528,13 +527,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
audioThread->start();
|
||||
|
||||
// Setup AssetClient
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
QThread* assetThread = new QThread;
|
||||
assetThread->setObjectName("Asset Thread");
|
||||
assetClient->moveToThread(assetThread);
|
||||
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
assetThread->start();
|
||||
ResourceManager::init();
|
||||
|
||||
// Setup MessagesClient
|
||||
auto messagesClient = DependencyManager::get<MessagesClient>();
|
||||
|
@ -644,13 +637,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
|
||||
identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
|
||||
|
||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
|
||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||
networkAccessManager.setCache(cache);
|
||||
|
||||
ResourceCache::setRequestLimit(3);
|
||||
|
||||
_glWidget = new GLCanvas();
|
||||
|
@ -661,15 +647,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
_glWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
_glWidget->setFocus();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// OSX doesn't seem to provide for hiding the cursor only on the GL widget
|
||||
_window->setCursor(Qt::BlankCursor);
|
||||
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
|
||||
#else
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible
|
||||
// when hovering over the window menu, which is a pain, so only hide it for
|
||||
// the GL surface
|
||||
_glWidget->setCursor(Qt::BlankCursor);
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// window menu, which is a pain, so only hide it for the GL surface
|
||||
auto cursorTarget = _glWidget;
|
||||
#endif
|
||||
cursorTarget->setCursor(Qt::BlankCursor);
|
||||
|
||||
// enable mouse tracking; otherwise, we only get drag events
|
||||
_glWidget->setMouseTracking(true);
|
||||
|
@ -981,6 +967,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_idleTimer->start(0);
|
||||
}
|
||||
|
||||
|
||||
void Application::checkChangeCursor() {
|
||||
QMutexLocker locker(&_changeCursorLock);
|
||||
if (_cursorNeedsChanging) {
|
||||
#ifdef Q_OS_MAC
|
||||
auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
|
||||
#else
|
||||
// On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
|
||||
// window menu, which is a pain, so only hide it for the GL surface
|
||||
auto cursorTarget = _glWidget;
|
||||
#endif
|
||||
cursorTarget->setCursor(_desiredCursor);
|
||||
|
||||
_cursorNeedsChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::showCursor(const QCursor& cursor) {
|
||||
QMutexLocker locker(&_changeCursorLock);
|
||||
_desiredCursor = cursor;
|
||||
_cursorNeedsChanging = true;
|
||||
}
|
||||
|
||||
void Application::aboutToQuit() {
|
||||
emit beforeAboutToQuit();
|
||||
|
||||
|
@ -1062,13 +1071,6 @@ void Application::cleanupBeforeQuit() {
|
|||
DependencyManager::destroy<OffscreenUi>();
|
||||
}
|
||||
|
||||
void Application::emptyLocalCache() {
|
||||
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
|
||||
cache->clear();
|
||||
}
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
EntityTreePointer tree = getEntities()->getTree();
|
||||
tree->setSimulation(NULL);
|
||||
|
@ -1106,11 +1108,7 @@ Application::~Application() {
|
|||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
|
||||
// cleanup the AssetClient thread
|
||||
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
|
||||
DependencyManager::destroy<AssetClient>();
|
||||
assetThread->quit();
|
||||
assetThread->wait();
|
||||
ResourceManager::cleanup();
|
||||
|
||||
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
|
||||
|
||||
|
@ -2431,6 +2429,9 @@ void Application::idle(uint64_t now) {
|
|||
return; // bail early, nothing to do here.
|
||||
}
|
||||
|
||||
|
||||
checkChangeCursor();
|
||||
|
||||
Stats::getInstance()->updateStats();
|
||||
AvatarInputs::getInstance()->update();
|
||||
|
||||
|
@ -3055,7 +3056,7 @@ void Application::reloadResourceCaches() {
|
|||
_viewFrustum.setOrientation(glm::quat());
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
|
||||
emptyLocalCache();
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
|
||||
DependencyManager::get<AnimationCache>()->refreshAll();
|
||||
DependencyManager::get<ModelCache>()->refreshAll();
|
||||
|
|
|
@ -120,6 +120,8 @@ public:
|
|||
QSize getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
|
||||
void showCursor(const QCursor& cursor);
|
||||
|
||||
bool isThrottleRendering() const;
|
||||
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
|
@ -328,8 +330,6 @@ private:
|
|||
|
||||
void cleanupBeforeQuit();
|
||||
|
||||
void emptyLocalCache();
|
||||
|
||||
void update(float deltaTime);
|
||||
|
||||
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
|
||||
|
@ -515,6 +515,11 @@ private:
|
|||
QTimer* _idleTimer { nullptr };
|
||||
|
||||
bool _fakedMouseEvent { false };
|
||||
|
||||
void checkChangeCursor();
|
||||
mutable QMutex _changeCursorLock { QMutex::Recursive };
|
||||
QCursor _desiredCursor{ Qt::BlankCursor };
|
||||
bool _cursorNeedsChanging { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -212,19 +212,21 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
|
||||
|
||||
//draw the mouse pointer
|
||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||
vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas...
|
||||
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
if (getReticleVisible()) {
|
||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||
vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas...
|
||||
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -335,9 +337,21 @@ QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) {
|
|||
|
||||
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();
|
||||
return _allowMouseCapture && qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown();
|
||||
}
|
||||
|
||||
void ApplicationCompositor::setAllowMouseCapture(bool capture) {
|
||||
if (qApp->isHMDMode()) {
|
||||
if (capture) {
|
||||
qApp->showCursor(Qt::BlankCursor);
|
||||
} else {
|
||||
qApp->showCursor(Qt::ArrowCursor);
|
||||
}
|
||||
}
|
||||
_allowMouseCapture = capture;
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::handleLeaveEvent() {
|
||||
|
||||
if (shouldCaptureMouse()) {
|
||||
|
|
|
@ -106,6 +106,9 @@ public:
|
|||
|
||||
bool shouldCaptureMouse() const;
|
||||
|
||||
bool getAllowMouseCapture() const { return _allowMouseCapture; }
|
||||
void setAllowMouseCapture(bool capture);
|
||||
|
||||
/// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false
|
||||
bool getReticleOverDesktop() const;
|
||||
void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
|
||||
|
@ -162,6 +165,8 @@ private:
|
|||
|
||||
bool _reticleOverQml { false };
|
||||
|
||||
bool _allowMouseCapture { true };
|
||||
|
||||
ReticleInterface* _reticleInterface;
|
||||
};
|
||||
|
||||
|
@ -173,12 +178,17 @@ class ReticleInterface : public QObject {
|
|||
Q_PROPERTY(float depth READ getDepth WRITE setDepth)
|
||||
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
|
||||
Q_PROPERTY(bool mouseCaptured READ isMouseCaptured)
|
||||
Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture)
|
||||
Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
|
||||
|
||||
public:
|
||||
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
|
||||
|
||||
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
|
||||
|
||||
Q_INVOKABLE bool getAllowMouseCapture() { return _compositor->getAllowMouseCapture(); }
|
||||
Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); }
|
||||
|
||||
Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); }
|
||||
|
||||
Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); }
|
||||
|
|
|
@ -9,23 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <functional>
|
||||
#include "DiskCacheEditor.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <AssetClient.h>
|
||||
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
|
||||
|
||||
}
|
||||
|
||||
QWindow* DiskCacheEditor::windowHandle() {
|
||||
|
@ -33,7 +31,6 @@ QWindow* DiskCacheEditor::windowHandle() {
|
|||
}
|
||||
|
||||
void DiskCacheEditor::toggle() {
|
||||
qDebug() << "DiskCacheEditor::toggle()";
|
||||
if (!_dialog) {
|
||||
makeDialog();
|
||||
}
|
||||
|
@ -88,17 +85,17 @@ void DiskCacheEditor::makeDialog() {
|
|||
Q_CHECK_PTR(_maxSize);
|
||||
_maxSize->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_maxSize, 2, 1, 1, 3);
|
||||
|
||||
|
||||
refresh();
|
||||
|
||||
|
||||
QPushButton* refreshCacheButton = new QPushButton(_dialog);
|
||||
Q_CHECK_PTR(refreshCacheButton);
|
||||
refreshCacheButton->setText("Refresh");
|
||||
refreshCacheButton->setToolTip("Reload the cache stats.");
|
||||
connect(refreshCacheButton, SIGNAL(clicked()), SLOT(refresh()));
|
||||
layout->addWidget(refreshCacheButton, 3, 2);
|
||||
|
||||
|
||||
|
||||
static const int REFRESH_INTERVAL = 100; // msec
|
||||
_refreshTimer = new QTimer(_dialog);
|
||||
_refreshTimer->setInterval(REFRESH_INTERVAL);
|
||||
_refreshTimer->setSingleShot(false);
|
||||
QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh);
|
||||
_refreshTimer->start();
|
||||
|
||||
QPushButton* clearCacheButton = new QPushButton(_dialog);
|
||||
Q_CHECK_PTR(clearCacheButton);
|
||||
clearCacheButton->setText("Clear");
|
||||
|
@ -108,7 +105,11 @@ void DiskCacheEditor::makeDialog() {
|
|||
}
|
||||
|
||||
void DiskCacheEditor::refresh() {
|
||||
static const std::function<QString(qint64)> stringify = [](qint64 number) {
|
||||
DependencyManager::get<AssetClient>()->cacheInfoRequest(this, "cacheInfoCallback");
|
||||
}
|
||||
|
||||
void DiskCacheEditor::cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize) {
|
||||
static const auto stringify = [](qint64 number) {
|
||||
static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB";
|
||||
static const qint64 CHUNK = 1024;
|
||||
QString unit;
|
||||
|
@ -122,30 +123,24 @@ void DiskCacheEditor::refresh() {
|
|||
}
|
||||
return QString("%0 %1").arg(number).arg(UNITS[i]);
|
||||
};
|
||||
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
|
||||
|
||||
if (_path) {
|
||||
_path->setText(cache->cacheDirectory());
|
||||
_path->setText(cacheDirectory);
|
||||
}
|
||||
if (_size) {
|
||||
_size->setText(stringify(cache->cacheSize()));
|
||||
_size->setText(stringify(cacheSize));
|
||||
}
|
||||
if (_maxSize) {
|
||||
_maxSize->setText(stringify(cache->maximumCacheSize()));
|
||||
_maxSize->setText(stringify(maximumCacheSize));
|
||||
}
|
||||
}
|
||||
|
||||
void DiskCacheEditor::clear() {
|
||||
QMessageBox::StandardButton buttonClicked =
|
||||
OffscreenUi::question(_dialog, "Clearing disk cache",
|
||||
"You are about to erase all the content of the disk cache, "
|
||||
"are you sure you want to do that?",
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache",
|
||||
"You are about to erase all the content of the disk cache, "
|
||||
"are you sure you want to do that?",
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
if (buttonClicked == QMessageBox::Ok) {
|
||||
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
|
||||
cache->clear();
|
||||
}
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
class QDialog;
|
||||
class QLabel;
|
||||
class QWindow;
|
||||
class QTimer;
|
||||
|
||||
class DiskCacheEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -32,8 +33,9 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void refresh();
|
||||
void cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize);
|
||||
void clear();
|
||||
|
||||
|
||||
private:
|
||||
void makeDialog();
|
||||
|
||||
|
@ -41,6 +43,7 @@ private:
|
|||
QPointer<QLabel> _path;
|
||||
QPointer<QLabel> _size;
|
||||
QPointer<QLabel> _maxSize;
|
||||
QPointer<QTimer> _refreshTimer;
|
||||
};
|
||||
|
||||
#endif // hifi_DiskCacheEditor_h
|
|
@ -47,22 +47,53 @@ AssetClient::AssetClient() {
|
|||
}
|
||||
|
||||
void AssetClient::init() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
// Setup disk cache if not already
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
if (!networkAccessManager.cache()) {
|
||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
|
||||
|
||||
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
|
||||
cache->setCacheDirectory(cachePath);
|
||||
networkAccessManager.setCache(cache);
|
||||
qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath
|
||||
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
|
||||
qDebug() << "ResourceManager disk cache setup at" << cachePath
|
||||
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection,
|
||||
Q_ARG(QObject*, reciever), Q_ARG(QString, slot));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (auto* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache())) {
|
||||
QMetaObject::invokeMethod(reciever, slot.toStdString().data(), Qt::QueuedConnection,
|
||||
Q_ARG(QString, cache->cacheDirectory()),
|
||||
Q_ARG(qint64, cache->cacheSize()),
|
||||
Q_ARG(qint64, cache->maximumCacheSize()));
|
||||
} else {
|
||||
qCWarning(asset_client) << "No disk cache to get info from.";
|
||||
}
|
||||
}
|
||||
|
||||
void AssetClient::clearCache() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "clearCache", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||
qDebug() << "AssetClient::clearCache(): Clearing disk cache.";
|
||||
cache->clear();
|
||||
} else {
|
||||
qCWarning(asset_client) << "No disk cache to clear.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,13 +43,17 @@ class AssetClient : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AssetClient();
|
||||
|
||||
Q_INVOKABLE void init();
|
||||
|
||||
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
|
||||
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
||||
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension);
|
||||
|
||||
public slots:
|
||||
void init();
|
||||
|
||||
void cacheInfoRequest(QObject* reciever, QString slot);
|
||||
void clearCache();
|
||||
|
||||
private slots:
|
||||
void handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
class AssetResourceRequest : public ResourceRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
|
||||
AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
|
||||
~AssetResourceRequest();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
class FileResourceRequest : public ResourceRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FileResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
|
||||
FileResourceRequest(const QUrl& url) : ResourceRequest(url) { }
|
||||
|
||||
protected:
|
||||
virtual void doSend() override;
|
||||
|
|
|
@ -28,6 +28,25 @@ HTTPResourceRequest::~HTTPResourceRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
void HTTPResourceRequest::setupTimer() {
|
||||
Q_ASSERT(!_sendTimer);
|
||||
static const int TIMEOUT_MS = 10000;
|
||||
|
||||
_sendTimer = new QTimer();
|
||||
connect(this, &QObject::destroyed, _sendTimer, &QTimer::deleteLater);
|
||||
connect(_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
|
||||
|
||||
_sendTimer->setSingleShot(true);
|
||||
_sendTimer->start(TIMEOUT_MS);
|
||||
}
|
||||
|
||||
void HTTPResourceRequest::cleanupTimer() {
|
||||
Q_ASSERT(_sendTimer);
|
||||
_sendTimer->disconnect(this);
|
||||
_sendTimer->deleteLater();
|
||||
_sendTimer = nullptr;
|
||||
}
|
||||
|
||||
void HTTPResourceRequest::doSend() {
|
||||
QNetworkRequest networkRequest(_url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -42,18 +61,15 @@ void HTTPResourceRequest::doSend() {
|
|||
|
||||
connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished);
|
||||
connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);
|
||||
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
|
||||
|
||||
static const int TIMEOUT_MS = 10000;
|
||||
_sendTimer.setSingleShot(true);
|
||||
_sendTimer.start(TIMEOUT_MS);
|
||||
setupTimer();
|
||||
}
|
||||
|
||||
void HTTPResourceRequest::onRequestFinished() {
|
||||
Q_ASSERT(_state == InProgress);
|
||||
Q_ASSERT(_reply);
|
||||
|
||||
_sendTimer.stop();
|
||||
cleanupTimer();
|
||||
|
||||
switch(_reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
|
@ -80,7 +96,7 @@ void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||
Q_ASSERT(_state == InProgress);
|
||||
|
||||
// We've received data, so reset the timer
|
||||
_sendTimer.start();
|
||||
_sendTimer->start();
|
||||
|
||||
emit progress(bytesReceived, bytesTotal);
|
||||
}
|
||||
|
@ -91,6 +107,8 @@ void HTTPResourceRequest::onTimeout() {
|
|||
_reply->abort();
|
||||
_reply->deleteLater();
|
||||
_reply = nullptr;
|
||||
|
||||
cleanupTimer();
|
||||
|
||||
_result = Timeout;
|
||||
_state = Finished;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
class HTTPResourceRequest : public ResourceRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HTTPResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
|
||||
HTTPResourceRequest(const QUrl& url) : ResourceRequest(url) { }
|
||||
~HTTPResourceRequest();
|
||||
|
||||
protected:
|
||||
|
@ -33,7 +33,10 @@ private slots:
|
|||
void onRequestFinished();
|
||||
|
||||
private:
|
||||
QTimer _sendTimer;
|
||||
void setupTimer();
|
||||
void cleanupTimer();
|
||||
|
||||
QTimer* _sendTimer { nullptr };
|
||||
QNetworkReply* _reply { nullptr };
|
||||
};
|
||||
|
||||
|
|
|
@ -11,12 +11,20 @@
|
|||
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include "AssetResourceRequest.h"
|
||||
#include "FileResourceRequest.h"
|
||||
#include "HTTPResourceRequest.h"
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QStandardPaths>
|
||||
#include <QThread>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
||||
#include "AssetResourceRequest.h"
|
||||
#include "FileResourceRequest.h"
|
||||
#include "HTTPResourceRequest.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
|
||||
QThread ResourceManager::_thread;
|
||||
ResourceManager::PrefixMap ResourceManager::_prefixMap;
|
||||
QMutex ResourceManager::_prefixMapLock;
|
||||
|
||||
|
@ -67,18 +75,41 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) {
|
|||
return url;
|
||||
}
|
||||
|
||||
void ResourceManager::init() {
|
||||
_thread.setObjectName("Ressource Manager Thread");
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
assetClient->moveToThread(&_thread);
|
||||
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
void ResourceManager::cleanup() {
|
||||
// cleanup the AssetClient thread
|
||||
DependencyManager::destroy<AssetClient>();
|
||||
_thread.quit();
|
||||
_thread.wait();
|
||||
}
|
||||
|
||||
ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const QUrl& url) {
|
||||
auto normalizedURL = normalizeURL(url);
|
||||
auto scheme = normalizedURL.scheme();
|
||||
|
||||
ResourceRequest* request = nullptr;
|
||||
|
||||
if (scheme == URL_SCHEME_FILE) {
|
||||
return new FileResourceRequest(parent, normalizedURL);
|
||||
request = new FileResourceRequest(normalizedURL);
|
||||
} else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) {
|
||||
return new HTTPResourceRequest(parent, normalizedURL);
|
||||
request = new HTTPResourceRequest(normalizedURL);
|
||||
} else if (scheme == URL_SCHEME_ATP) {
|
||||
return new AssetResourceRequest(parent, normalizedURL);
|
||||
request = new AssetResourceRequest(normalizedURL);
|
||||
} else {
|
||||
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
|
||||
return nullptr;
|
||||
}
|
||||
Q_ASSERT(request);
|
||||
|
||||
qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
|
||||
|
||||
return nullptr;
|
||||
request->moveToThread(&_thread);
|
||||
return request;
|
||||
}
|
||||
|
|
|
@ -29,8 +29,15 @@ public:
|
|||
static void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
|
||||
static QString normalizeURL(const QString& urlString);
|
||||
static QUrl normalizeURL(const QUrl& url);
|
||||
|
||||
static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url);
|
||||
|
||||
static void init();
|
||||
static void cleanup();
|
||||
|
||||
private:
|
||||
static QThread _thread;
|
||||
|
||||
using PrefixMap = std::map<QString, QString>;
|
||||
|
||||
static PrefixMap _prefixMap;
|
||||
|
|
|
@ -11,12 +11,15 @@
|
|||
|
||||
#include "ResourceRequest.h"
|
||||
|
||||
ResourceRequest::ResourceRequest(QObject* parent, const QUrl& url) :
|
||||
QObject(parent),
|
||||
_url(url) {
|
||||
}
|
||||
#include <QtCore/QThread>
|
||||
|
||||
ResourceRequest::ResourceRequest(const QUrl& url) : _url(url) { }
|
||||
|
||||
void ResourceRequest::send() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "send", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(_state == NotStarted);
|
||||
|
||||
_state = InProgress;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
class ResourceRequest : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ResourceRequest(QObject* parent, const QUrl& url);
|
||||
ResourceRequest(const QUrl& url);
|
||||
|
||||
enum State {
|
||||
NotStarted = 0,
|
||||
|
@ -38,7 +38,6 @@ public:
|
|||
NotFound
|
||||
};
|
||||
|
||||
void send();
|
||||
QByteArray getData() { return _data; }
|
||||
State getState() const { return _state; }
|
||||
Result getResult() const { return _result; }
|
||||
|
@ -47,8 +46,11 @@ public:
|
|||
|
||||
void setCacheEnabled(bool value) { _cacheEnabled = value; }
|
||||
|
||||
public slots:
|
||||
void send();
|
||||
|
||||
signals:
|
||||
void progress(uint64_t bytesReceived, uint64_t bytesTotal);
|
||||
void progress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void finished();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -96,6 +96,9 @@
|
|||
if (this.ballLocked === true) {
|
||||
return;
|
||||
}
|
||||
if(this.ball!==null){
|
||||
Entities.deleteEntity(this.ball);
|
||||
}
|
||||
|
||||
var properties = {
|
||||
name: 'Hifi Tilt Maze Ball',
|
||||
|
|
Loading…
Reference in a new issue