Merge remote-tracking branch 'upstream/master' into plugins

Conflicts:
	interface/src/Application.cpp
	interface/src/devices/OculusManager.cpp
	interface/src/devices/OculusManager.h
This commit is contained in:
Brad Davis 2015-06-23 16:54:32 -07:00
commit 8f8bda327c
40 changed files with 391 additions and 586 deletions

View file

@ -181,7 +181,7 @@ function entityCollisionWithEntity(entity1, entity2, collision) {
}
function shootBullet(position, velocity, grenade) {
var BULLET_SIZE = 0.10;
var BULLET_SIZE = .09;
var BULLET_LIFETIME = 10.0;
var BULLET_GRAVITY = -0.25;
var GRENADE_VELOCITY = 15.0;

View file

@ -10,17 +10,16 @@
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include('lineRider.js')
var MAX_POINTS_PER_LINE = 30;
var LINE_LIFETIME = 60 * 5 //5 minute lifetime
var LINE_DIMENSIONS = 5;
var LIFETIME = 6000;
var colorPalette = [{
red: 236,
green: 208,
blue: 120
}, {
red: 217,
red: 214,
green: 91,
blue: 67
}, {
@ -40,15 +39,6 @@ var colorPalette = [{
var currentColorIndex = 0;
var currentColor = colorPalette[currentColorIndex];
if (hydraCheck() === true) {
HydraPaint();
} else {
MousePaint();
}
function cycleColor() {
currentColor = colorPalette[++currentColorIndex];
if (currentColorIndex === colorPalette.length - 1) {
@ -57,42 +47,17 @@ function cycleColor() {
}
function hydraCheck() {
var numberOfButtons = Controller.getNumberOfButtons();
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
return hydrasConnected; //hydrasConnected;
}
//************ Mouse Paint **************************
MousePaint();
function MousePaint() {
var DRAWING_DISTANCE = 2;
var DRAWING_DISTANCE = 5;
var lines = [];
var deletedLines = [];
var isDrawing = false;
var path = [];
var lineRider = new LineRider();
lineRider.addStartHandler(function() {
var points = [];
//create points array from list of all points in path
path.forEach(function(point) {
points.push(point);
});
lineRider.setPath(points);
});
var LINE_WIDTH = 7;
var line;
var points = [];
var line, linePosition;
var BRUSH_SIZE = 0.02;
var BRUSH_SIZE = .05;
var brush = Entities.addEntity({
type: 'Sphere',
@ -110,51 +75,42 @@ function MousePaint() {
});
function newLine(point) {
function newLine(position) {
linePosition = position;
line = Entities.addEntity({
position: MyAvatar.position,
position: position,
type: "Line",
color: currentColor,
dimensions: {
x: 10,
y: 10,
z: 10
x: LINE_DIMENSIONS,
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
linePoints: [],
lineWidth: LINE_WIDTH,
lifetime: LINE_LIFETIME
lifetime: LIFETIME
});
points = [];
if (point) {
points.push(point);
path.push(point);
}
lines.push(line);
}
function mouseMoveEvent(event) {
var worldPoint = computeWorldPoint(event);
Entities.editEntity(brush, {
position: worldPoint
});
if (!isDrawing) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
var point = Vec3.sum(Camera.getPosition(), addVector);
points.push(point);
path.push(point);
Entities.editEntity(line, {
linePoints: points
});
Entities.editEntity(brush, {
position: point
});
var localPoint = computeLocalPoint(event)
var success = Entities.appendPoint(line, localPoint);
if (points.length === MAX_POINTS_PER_LINE) {
//We need to start a new line!
newLine(point);
if (!success) {
newLine(worldPoint);
Entities.appendPoint(line, computeLocalPoint(event));
}
}
@ -171,17 +127,26 @@ function MousePaint() {
lines.push(restoredLine);
}
function computeWorldPoint(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
return Vec3.sum(Camera.getPosition(), addVector);
}
function computeLocalPoint(event) {
var localPoint = Vec3.subtract(computeWorldPoint(event), linePosition);
return localPoint;
}
function mousePressEvent(event) {
if(!event.isLeftButton) {
if (!event.isLeftButton) {
isDrawing = false;
return;
}
lineRider.mousePressEvent(event);
path = [];
newLine();
newLine(computeWorldPoint(event));
isDrawing = true;
}
function mouseReleaseEvent() {
@ -198,21 +163,21 @@ function MousePaint() {
if (event.text === "z") {
undoStroke();
}
if(event.text === "x") {
if (event.text === "x") {
redoStroke();
}
}
function cleanup() {
lines.forEach(function(line) {
Entities.deleteEntity(line);
// Entities.deleteEntity(line);
});
Entities.deleteEntity(brush);
lineRider.cleanup();
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
@ -222,266 +187,6 @@ function MousePaint() {
}
//*****************HYDRA PAINT *******************************************
function HydraPaint() {
var lineRider = new LineRider();
lineRider.addStartHandler(function() {
var points = [];
//create points array from list of all points in path
rightController.path.forEach(function(point) {
points.push(point);
});
lineRider.setPath(points);
});
var LEFT = 0;
var RIGHT = 1;
var currentTime = 0;
var minBrushSize = .02;
var maxBrushSize = .04
var minLineWidth = 5;
var maxLineWidth = 10;
var currentLineWidth = minLineWidth;
var MIN_PAINT_TRIGGER_THRESHOLD = .01;
var COLOR_CHANGE_TIME_FACTOR = 0.1;
var RIGHT_BUTTON_1 = 7
var RIGHT_BUTTON_2 = 8
var RIGHT_BUTTON_3 = 9;
var RIGHT_BUTTON_4 = 10
var LEFT_BUTTON_1 = 1;
var LEFT_BUTTON_2 = 2;
var LEFT_BUTTON_3 = 3;
var LEFT_BUTTON_4 = 4;
var STROKE_SMOOTH_FACTOR = 1;
var MIN_DRAW_DISTANCE = 0.2;
var MAX_DRAW_DISTANCE = 0.4;
function controller(side, undoButton, redoButton, cycleColorButton, startRideButton) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.palm = 2 * side;
this.tip = 2 * side + 1;
this.trigger = side;
this.lines = [];
this.deletedLines = [] //just an array of properties objects
this.isPainting = false;
this.undoButton = undoButton;
this.undoButtonPressed = false;
this.prevUndoButtonPressed = false;
this.redoButton = redoButton;
this.redoButtonPressed = false;
this.prevRedoButtonPressed = false;
this.cycleColorButton = cycleColorButton;
this.cycleColorButtonPressed = false;
this.prevColorCycleButtonPressed = false;
this.startRideButton = startRideButton;
this.startRideButtonPressed = false;
this.prevStartRideButtonPressed = false;
this.strokeCount = 0;
this.currentBrushSize = minBrushSize;
this.points = [];
this.path = [];
this.brush = Entities.addEntity({
type: 'Sphere',
position: {
x: 0,
y: 0,
z: 0
},
color: currentColor,
dimensions: {
x: minBrushSize,
y: minBrushSize,
z: minBrushSize
}
});
this.newLine = function(point) {
this.line = Entities.addEntity({
position: MyAvatar.position,
type: "Line",
color: currentColor,
dimensions: {
x: 10,
y: 10,
z: 10
},
lineWidth: 5,
lifetime: LINE_LIFETIME
});
this.points = [];
if (point) {
this.points.push(point);
this.path.push(point);
}
this.lines.push(this.line);
}
this.update = function(deltaTime) {
this.updateControllerState();
this.avatarPalmOffset = Vec3.subtract(this.palmPosition, MyAvatar.position);
this.projectedForwardDistance = Vec3.dot(Quat.getFront(Camera.getOrientation()), this.avatarPalmOffset);
this.mappedPalmOffset = map(this.projectedForwardDistance, -.5, .5, MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
this.tipDirection = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition));
this.offsetVector = Vec3.multiply(this.mappedPalmOffset, this.tipDirection);
this.drawPoint = Vec3.sum(this.palmPosition, this.offsetVector);
this.currentBrushSize = map(this.triggerValue, 0, 1, minBrushSize, maxBrushSize);
Entities.editEntity(this.brush, {
position: this.drawPoint,
dimensions: {
x: this.currentBrushSize,
y: this.currentBrushSize,
z: this.currentBrushSize
},
color: currentColor
});
if (this.triggerValue > MIN_PAINT_TRIGGER_THRESHOLD) {
if (!this.isPainting) {
this.isPainting = true;
this.newLine();
this.path = [];
}
if (this.strokeCount % STROKE_SMOOTH_FACTOR === 0) {
this.paint(this.drawPoint);
}
this.strokeCount++;
} else if (this.triggerValue < MIN_PAINT_TRIGGER_THRESHOLD && this.isPainting) {
this.releaseTrigger();
}
this.oldPalmPosition = this.palmPosition;
this.oldTipPosition = this.tipPosition;
}
this.releaseTrigger = function() {
this.isPainting = false;
}
this.updateControllerState = function() {
this.undoButtonPressed = Controller.isButtonPressed(this.undoButton);
this.redoButtonPressed = Controller.isButtonPressed(this.redoButton);
this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);
this.startRideButtonPressed = Controller.isButtonPressed(this.startRideButton);
//This logic gives us button release
if (this.prevUndoButtonPressed === true && this.undoButtonPressed === false) {
//User released undo button, so undo
this.undoStroke();
}
if (this.prevRedoButtonPressed === true && this.redoButtonPressed === false) {
this.redoStroke();
}
if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
cycleColor();
Entities.editEntity(this.brush, {
color: currentColor
});
}
if (this.prevStartRideButtonPressed === true && this.startRideButtonPressed === false) {
lineRider.toggleRide();
}
this.prevRedoButtonPressed = this.redoButtonPressed;
this.prevUndoButtonPressed = this.undoButtonPressed;
this.prevCycleColorButtonPressed = this.cycleColorButtonPressed;
this.prevStartRideButtonPressed = this.startRideButtonPressed;
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
this.triggerValue = Controller.getTriggerValue(this.trigger);
}
this.undoStroke = function() {
var deletedLine = this.lines.pop();
var deletedLineProps = Entities.getEntityProperties(deletedLine);
this.deletedLines.push(deletedLineProps);
Entities.deleteEntity(deletedLine);
}
this.redoStroke = function() {
var restoredLine = Entities.addEntity(this.deletedLines.pop());
Entities.addEntity(restoredLine);
this.lines.push(restoredLine);
}
this.paint = function(point) {
currentLineWidth = map(this.triggerValue, 0, 1, minLineWidth, maxLineWidth);
this.points.push(point);
this.path.push(point);
Entities.editEntity(this.line, {
linePoints: this.points,
lineWidth: currentLineWidth,
});
if (this.points.length > MAX_POINTS_PER_LINE) {
this.newLine(point);
}
}
this.cleanup = function() {
Entities.deleteEntity(this.brush);
this.lines.forEach(function(line) {
Entities.deleteEntity(line);
});
}
}
function update(deltaTime) {
rightController.update(deltaTime);
leftController.update(deltaTime);
currentTime += deltaTime;
}
function cleanup() {
rightController.cleanup();
leftController.cleanup();
lineRider.cleanup();
}
function mousePressEvent(event) {
lineRider.mousePressEvent(event);
}
function vectorIsZero(v) {
return v.x === 0 && v.y === 0 && v.z === 0;
}
var rightController = new controller(RIGHT, RIGHT_BUTTON_3, RIGHT_BUTTON_4, RIGHT_BUTTON_1, RIGHT_BUTTON_2);
var leftController = new controller(LEFT, LEFT_BUTTON_3, LEFT_BUTTON_4, LEFT_BUTTON_1, LEFT_BUTTON_2);
Script.update.connect(update);
Script.scriptEnding.connect(cleanup);
Controller.mousePressEvent.connect(mousePressEvent);
}
function randFloat(low, high) {
return low + Math.random() * (high - low);
}

View file

@ -9,48 +9,52 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
function hslToRgb(hslColor) {
var h = hslColor.hue;
var s = hslColor.sat;
var l = hslColor.light;
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
map = function(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
hslToRgb = function(hslColor) {
var h = hslColor.hue;
var s = hslColor.sat;
var l = hslColor.light;
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
red: Math.round(r * 255),
green: Math.round(g * 255),
blue: Math.round(b * 255)
};
return {
red: Math.round(r * 255),
green: Math.round(g * 255),
blue: Math.round(b * 255)
};
}
function map(value, min1, max1, min2, max2) {
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
}
function randFloat(low, high) {
return low + Math.random() * (high - low);
randFloat = function(low, high) {
return low + Math.random() * (high - low);
}
function randInt(low, high) {
return Math.floor(randFloat(low, high));
randInt = function(low, high) {
return Math.floor(randFloat(low, high));
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
<g>
<g>
<path fill="#7E7E7E" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 501 B

View file

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

View file

@ -1,3 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="344 454 26 74" width="26pt" height="74pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2015-06-12 18:23Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="1728" height="1466"/><g><title> Navi Bar</title><line x1="356.58927" y1="466.42861" x2="356.58927" y2="515.4286" stroke="#b3b3b3" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="3"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 778 B

View file

@ -46,6 +46,36 @@ DialogContainer {
property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area
property int inputAreaStep: (height - inputAreaHeight) / 2
MouseArea {
// Drag the icon
width: parent.height
height: parent.height
x: 0
y: 0
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}
MouseArea {
// Drag the input rectangle
width: parent.width - parent.height
height: parent.inputAreaHeight
x: parent.height
y: parent.inputAreaStep
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}
Image {
id: backArrow
@ -71,7 +101,7 @@ DialogContainer {
Image {
id: forwardArrow
source: addressBarDialog.forwardEnabled ? "../images/darkgreyarrow.svg" : "../images/redarrow.svg"
source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/redarrow.svg"
anchors {
fill: parent
@ -111,38 +141,7 @@ DialogContainer {
addressBarDialog.loadAddress(addressLine.text)
}
}
MouseArea {
// Drag the icon
width: parent.height
height: parent.height
x: 0
y: 0
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}
// Add this code to make text bar draggable
/*
MouseArea {
// Drag the input rectangle
width: parent.width - parent.height
height: parent.inputAreaHeight
x: parent.height
y: parent.inputAreaStep
drag {
target: root
minimumX: -parent.inputAreaStep
minimumY: -parent.inputAreaStep
maximumX: root.parent ? root.maximumX : 0
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
}
}*/
}
}

View file

@ -7,8 +7,9 @@ Hifi.Tooltip {
id: root
HifiConstants { id: hifi }
// FIXME adjust position based on the edges of the screen
x: lastMousePosition.x + 20
y: lastMousePosition.y + 5
x: (lastMousePosition.x > surfaceSize.width/2) ? lastMousePosition.x - 140 : lastMousePosition.x + 20
//y: lastMousePosition.y + 5
y: (lastMousePosition.y > surfaceSize.height/2) ? lastMousePosition.y - 70 : lastMousePosition.y + 5
implicitWidth: border.implicitWidth
implicitHeight: border.implicitHeight

View file

@ -1094,6 +1094,7 @@ void Application::resizeGL() {
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto uiSize = displayPlugin->getCanvasSize();
if (offscreenUi->getWindow()->geometry().size() != fromGlm(uiSize)) {
offscreenUi->resize(fromGlm(uiSize));
@ -4743,3 +4744,10 @@ mat4 Application::getEyePose(int eye) const {
return mat4();
}
mat4 Application::getHeadPose() const {
if (isHMDMode()) {
return OculusManager::getHeadPose();
}
return mat4();
}

View file

@ -107,6 +107,7 @@ Avatar::Avatar() :
}
Avatar::~Avatar() {
assert(_motionState == nullptr);
for(auto attachment : _unusedAttachments) {
delete attachment;
}

View file

@ -114,6 +114,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// DO NOT update or fade out uninitialized Avatars
++avatarIterator;
} else if (avatar->shouldDie()) {
removeAvatarMotionState(avatar);
_avatarFades.push_back(avatarIterator.value());
avatarIterator = _avatarHash.erase(avatarIterator);
} else {
@ -163,12 +164,13 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
}
// protected
void AvatarManager::removeAvatarMotionState(Avatar* avatar) {
AvatarMotionState* motionState= avatar->_motionState;
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
AvatarMotionState* motionState= rawPointer->_motionState;
if (motionState) {
// clean up physics stuff
motionState->clearObjectBackPointer();
avatar->_motionState = nullptr;
rawPointer->_motionState = nullptr;
_avatarMotionStates.remove(motionState);
_motionStatesToAdd.remove(motionState);
_motionStatesToDelete.push_back(motionState);
@ -181,7 +183,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
if (avatarIterator != _avatarHash.end()) {
std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value());
if (avatar != _myAvatar && avatar->isInitialized()) {
removeAvatarMotionState(avatar.get());
removeAvatarMotionState(avatar);
_avatarFades.push_back(avatarIterator.value());
_avatarHash.erase(avatarIterator);
}
@ -197,7 +199,7 @@ void AvatarManager::clearOtherAvatars() {
// don't remove myAvatar or uninitialized avatars from the list
++avatarIterator;
} else {
removeAvatarMotionState(avatar.get());
removeAvatarMotionState(avatar);
_avatarFades.push_back(avatarIterator.value());
avatarIterator = _avatarHash.erase(avatarIterator);
}

View file

@ -73,7 +73,7 @@ private:
// virtual overrides
virtual AvatarSharedPointer newSharedAvatar();
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
void removeAvatarMotionState(Avatar* avatar);
void removeAvatarMotionState(AvatarSharedPointer avatar);
virtual void removeAvatar(const QUuid& sessionUUID);
QVector<AvatarSharedPointer> _avatarFades;

View file

@ -334,12 +334,12 @@ void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& or
// Intersection UI overlay space
glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;
glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius;
intersectionWithUi += overlayPosition;
glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition;
glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition;
// Intersection in world space
origin = overlayPosition;
direction = glm::normalize(intersectionWithUi - origin);
origin = worldSpaceHeadPosition;
direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);
}
//Caculate the click location using one of the sixense controllers. Scale is not applied
@ -394,42 +394,13 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
//If we are in oculus, render reticle later
if (_lastMouseMove == 0) {
_lastMouseMove = usecTimestampNow();
}
QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());
static const int MAX_IDLE_TIME = 3;
if (_reticlePosition[MOUSE] != position) {
_lastMouseMove = usecTimestampNow();
} else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) {
//float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians
//OculusManager::getEulerAngles(yaw, pitch, roll);
glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll));
glm::vec3 result;
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
if (calculateRayUICollisionPoint(myAvatar->getEyePosition(),
myAvatar->getOrientation() * orientation * IDENTITY_FRONT,
result)) {
glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition());
glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection));
glm::vec2 screenPos = sphericalToScreen(spericalPos);
position = QPoint(screenPos.x, screenPos.y);
// FIXME
//glCanvas->cursor().setPos(glCanvas->mapToGlobal(position));
} else {
qDebug() << "No collision point";
}
}
_reticlePosition[MOUSE] = position;
_reticleActive[MOUSE] = true;
_magActive[MOUSE] = _magnifier;
_reticleActive[LEFT_CONTROLLER] = false;
_reticleActive[RIGHT_CONTROLLER] = false;
} else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
_lastMouseMove = 0;
//only render controller pointer if we aren't already rendering a mouse pointer
_reticleActive[MOUSE] = false;
_magActive[MOUSE] = false;

View file

@ -97,7 +97,6 @@ private:
QPoint _reticlePosition[NUMBER_OF_RETICLES];
bool _magActive[NUMBER_OF_RETICLES];
float _magSizeMult[NUMBER_OF_RETICLES];
quint64 _lastMouseMove{ 0 };
bool _magnifier{ true };
float _alpha{ 1.0f };

View file

@ -80,6 +80,7 @@ void TextOverlay::render(RenderArgs* args) {
glm::vec2 topLeft(left, top);
glm::vec2 bottomRight(right, bottom);
glBindTexture(GL_TEXTURE_2D, 0);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
const int leftAdjust = -1; // required to make text render relative to left edge of bounds

View file

@ -795,6 +795,11 @@ void AudioClient::handleAudioInput() {
delete[] inputAudioSamples;
// Remove DC offset
if (!_isStereoInput && !_audioSourceInjectEnabled) {
_inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples);
}
// only impose the noise gate and perform tone injection if we are sending mono audio
if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) {
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);

View file

@ -33,6 +33,32 @@ AudioNoiseGate::AudioNoiseGate() :
}
void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
//
// DC Offset correction
//
// Measure the DC offset over a trailing number of frames, and remove it from the input signal.
// This causes the noise background measurements and server muting to be more accurate. Many off-board
// ADC's have a noticeable DC offset.
//
const float DC_OFFSET_AVERAGING = 0.99f;
float measuredDcOffset = 0.0f;
// Remove trailing DC offset from samples
for (int i = 0; i < numSamples; i++) {
measuredDcOffset += samples[i];
samples[i] -= (int16_t) _dcOffset;
}
// Update measured DC offset
measuredDcOffset /= numSamples;
if (_dcOffset == 0.0f) {
// On first frame, copy over measured offset
_dcOffset = measuredDcOffset;
} else {
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
}
}
void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
//
// Impose Noise Gate
@ -61,17 +87,12 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
const float DC_OFFSET_AVERAGING = 0.99f;
// Check clipping, adjust DC offset, and check if should open noise gate
float measuredDcOffset = 0.0f;
// Check clipping, and check if should open noise gate
_didClipInLastFrame = false;
for (int i = 0; i < numSamples; i++) {
measuredDcOffset += samples[i];
samples[i] -= (int16_t) _dcOffset;
thisSample = std::abs(samples[i]);
if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
_didClipInLastFrame = true;
}
@ -83,14 +104,6 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
}
}
measuredDcOffset /= numSamples;
if (_dcOffset == 0.0f) {
// On first frame, copy over measured offset
_dcOffset = measuredDcOffset;
} else {
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
}
_lastLoudness = fabs(loudness / numSamples);
if (_quietestFrame > _lastLoudness) {

View file

@ -21,6 +21,7 @@ public:
AudioNoiseGate();
void gateSamples(int16_t* samples, int numSamples);
void removeDCOffset(int16_t* samples, int numSamples);
bool clippedInLastFrame() const { return _didClipInLastFrame; }
float getMeasuredFloor() const { return _measuredFloor; }

View file

@ -7,4 +7,10 @@ add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(networking shared)
# we use libsoxr for resampling
add_dependency_external_projects(soxr)
find_package(Soxr REQUIRED)
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
link_hifi_libraries(networking shared)

View file

@ -16,10 +16,12 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
#include <soxr.h>
#include "AbstractAudioInterface.h"
#include "AudioRingBuffer.h"
#include "AudioLogging.h"
#include "SoundCache.h"
#include "AudioInjector.h"
@ -284,3 +286,66 @@ void AudioInjector::stopAndDeleteLater() {
stop();
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}
AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) {
if (soundUrl.isEmpty()) {
return NULL;
}
auto soundCache = DependencyManager::get<SoundCache>();
if (soundCache.isNull()) {
return NULL;
}
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(soundUrl));
if (sound.isNull() || !sound->isReady()) {
return NULL;
}
AudioInjectorOptions options;
options.stereo = sound->isStereo();
options.position = position;
options.volume = volume;
QByteArray samples = sound->getByteArray();
if (stretchFactor == 1.0f) {
return playSound(samples, options, NULL);
}
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
const int channelCount = sound->isStereo() ? 2 : 1;
const int standardRate = AudioConstants::SAMPLE_RATE;
const int resampledRate = standardRate * stretchFactor;
const int nInputSamples = samples.size() / sizeof(int16_t);
const int nOutputSamples = nInputSamples * stretchFactor;
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
receivedSamples, nInputSamples, NULL,
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
&spec, &qualitySpec, 0);
if (soxError) {
qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
resampled = samples;
}
return playSound(resampled, options, NULL);
}
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(buffer, options);
injector->setLocalAudioInterface(localInterface);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
return injector;
}

View file

@ -45,6 +45,10 @@ public:
bool isLocalOnly() const { return _options.localOnly; }
void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; }
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface);
static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position);
public slots:
void injectAudio();
void restart();

View file

@ -17,10 +17,4 @@ find_package(PolyVox REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
# for changing the pitch of collision sounds
add_dependency_external_projects(soxr)
find_package(Soxr REQUIRED)
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
link_hifi_libraries(shared gpu script-engine render render-utils)

View file

@ -27,10 +27,6 @@
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <TextureCache.h>
#include <SoundCache.h>
#include <soxr.h>
#include <AudioConstants.h>
#include "EntityTreeRenderer.h"
@ -48,16 +44,12 @@
#include "RenderablePolyVoxEntityItem.h"
#include "EntitiesRendererLogging.h"
#include "DependencyManager.h"
#include "AddressManager.h"
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :
OctreeRenderer(),
_wantScripts(wantScripts),
_entitiesScriptEngine(NULL),
_sandboxScriptEngine(NULL),
_localAudioInterface(NULL),
_lastMouseEventValid(false),
_viewState(viewState),
_scriptingServices(scriptingServices),
@ -1057,7 +1049,6 @@ void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
}
}
void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) {
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
if (!entity) {
@ -1100,61 +1091,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) {
return;
}
auto soundCache = DependencyManager::get<SoundCache>();
if (soundCache.isNull()) {
return;
}
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(collisionSoundURL));
if (sound.isNull() || !sound->isReady()) {
return;
}
// This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all.
// Quiet sound aren't really heard at all, so we can compress everything to the range [1-c, 1], if we play it all.
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
float volume = energyFactorOfFull;
volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
// This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored.
AudioInjectorOptions options;
options.stereo = sound->isStereo();
options.position = position;
options.volume = volume;
const float volume = (energyFactorOfFull * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
QByteArray samples = sound->getByteArray();
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
const int channelCount = sound->isStereo() ? 2 : 1;
const float factor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
const int standardRate = AudioConstants::SAMPLE_RATE;
const int resampledRate = standardRate * factor;
const int nInputSamples = samples.size() / sizeof(int16_t);
const int nOutputSamples = nInputSamples * factor;
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
receivedSamples, nInputSamples, NULL,
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
&spec, &qualitySpec, 0);
if (soxError) {
qCDebug(entitiesrenderer) << "Unable to resample" << collisionSoundURL << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
resampled = samples;
}
AudioInjector* injector = new AudioInjector(resampled, options);
injector->setLocalAudioInterface(_localAudioInterface);
injector->triggerDeleteAfterFinish();
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
}
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,

View file

@ -158,7 +158,6 @@ private:
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision);
AbstractAudioInterface* _localAudioInterface; // So we can render collision sounds
bool _lastMouseEventValid;
MouseEvent _lastMouseEvent;

View file

@ -43,9 +43,9 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
// TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning.
//batch.setModelTransform(getTransformToCenter());
batch.setModelTransform(Transform());
Transform transform = Transform();
transform.setTranslation(getPosition());
batch.setModelTransform(transform);
batch._glLineWidth(getLineWidth());
if (getLinePoints().size() > 1) {

View file

@ -197,6 +197,8 @@ public:
QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); }
void setVoxelDataDirty() { _voxelDataChanged = true; }
void setLinePointsDirty() {_linePointsChanged = true; }
void setCreated(QDateTime& v);

View file

@ -442,6 +442,43 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
return true;
}
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor) {
if (!_entityTree) {
return false;
}
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
}
EntityTypes::EntityType entityType = entity->getType();
if (entityType != EntityTypes::Line) {
return false;
}
auto now = usecTimestampNow();
LineEntityItem* lineEntity = static_cast<LineEntityItem*>(entity.get());
_entityTree->lockForWrite();
bool success = actor(*lineEntity);
entity->setLastEdited(now);
entity->setLastBroadcast(now);
_entityTree->unlock();
_entityTree->lockForRead();
EntityItemProperties properties = entity->getProperties();
_entityTree->unlock();
properties.setLinePointsDirty();
properties.setLastEdited(now);
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
return success;
}
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
polyVoxEntity.setSphere(center, radius, value);
@ -460,6 +497,21 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
});
}
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool
{
return lineEntity.setLinePoints(points);
});
}
bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) {
return setPoints(entityID, [point](LineEntityItem& lineEntity) -> bool
{
return lineEntity.appendPoint(point);
});
}
bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
std::function<bool(EntitySimulation*, EntityItemPointer)> actor) {

View file

@ -22,6 +22,7 @@
#include <OctreeScriptingInterface.h>
#include <RegisteredMetaTypes.h>
#include "PolyVoxEntityItem.h"
#include "LineEntityItem.h"
#include "EntityEditPacketSender.h"
@ -121,6 +122,9 @@ public slots:
Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value);
Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value);
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector<glm::vec3>& points);
Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point);
Q_INVOKABLE void dumpTree() const;
@ -157,6 +161,7 @@ signals:
private:
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
bool setVoxels(QUuid entityID, std::function<void(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode

View file

@ -23,6 +23,7 @@
const float LineEntityItem::DEFAULT_LINE_WIDTH = 2.0f;
const int LineEntityItem::MAX_POINTS_PER_LINE = 70;
EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
@ -85,24 +86,38 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) {
return somethingChanged;
}
void LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
QVector<glm::vec3> sanitizedPoints;
int invalidPoints = 0;
bool LineEntityItem::appendPoint(const glm::vec3& point) {
if (_points.size() > MAX_POINTS_PER_LINE - 1) {
qDebug() << "MAX POINTS REACHED!";
return false;
}
glm::vec3 halfBox = getDimensions() * 0.5f;
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
qDebug() << "Point is outside entity's bounding box";
return false;
}
_points << point;
_pointsChanged = true;
return true;
}
bool LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
if (points.size() > MAX_POINTS_PER_LINE) {
return false;
}
for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points.at(i);
// Make sure all of our points are valid numbers.
// Must be greater than 0 because vector component is set to 0 if it is invalid data. Also should never be greater than TREE_SCALE
if ( (point.x > 0 && point.x < TREE_SCALE) && (point.y > 0 && point.y < TREE_SCALE) && (point.z > 0 && point.z < TREE_SCALE) ) {
sanitizedPoints << point;
} else {
++invalidPoints;
glm::vec3 pos = getPosition();
glm::vec3 halfBox = getDimensions() * 0.5f;
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
qDebug() << "Point is outside entity's bounding box";
return false;
}
}
if (invalidPoints > 0) {
qDebug() << "Line with" << invalidPoints << "INVALID POINTS";
}
_points = sanitizedPoints;
_points = points;
_pointsChanged = true;
return true;
}
int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,

View file

@ -54,7 +54,8 @@ class LineEntityItem : public EntityItem {
void setLineWidth(float lineWidth){ _lineWidth = lineWidth; }
float getLineWidth() const{ return _lineWidth; }
void setLinePoints(const QVector<glm::vec3>& points);
bool setLinePoints(const QVector<glm::vec3>& points);
bool appendPoint(const glm::vec3& point);
const QVector<glm::vec3>& getLinePoints() const{ return _points; }
@ -68,6 +69,7 @@ class LineEntityItem : public EntityItem {
virtual void debugDump() const;
static const float DEFAULT_LINE_WIDTH;
static const int MAX_POINTS_PER_LINE;
protected:
rgbColor _color;

View file

@ -308,6 +308,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
handlePath(returnedPath, trigger);
}
} else {
// we're going to hit the index path, set that as the _newHostLookupPath
_newHostLookupPath = INDEX_PATH;
// we didn't override the path or get one back - ask the DS for the viewpoint of its index path
// which we will jump to if it exists
emit pathChangeRequired(INDEX_PATH);
@ -479,6 +482,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
// We use _newHostLookupPath to determine if the client has already stored its last address
// before moving to a new host thanks to the information in the same lookup URL.
if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) {
addCurrentAddressToHistory(LookupTrigger::UserInput);
}

View file

@ -350,6 +350,19 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
if (rule.extensionFormat.contains("%N")) {
if (rule.maxBackupVersions > 0) {
qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "...";
// Delete maximum rolling file because rename() fails on Windows if target exists
QString backupMaxExtensionN = rule.extensionFormat;
backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions));
QString backupMaxFilenameN = _filename + backupMaxExtensionN;
QFile backupMaxFileN(backupMaxFilenameN);
if (backupMaxFileN.exists()) {
int result = remove(qPrintable(backupMaxFilenameN));
if (result != 0) {
qCDebug(octree) << "ERROR deleting old backup file " << backupMaxFilenameN;
}
}
for(int n = rule.maxBackupVersions - 1; n > 0; n--) {
QString backupExtensionN = rule.extensionFormat;
QString backupExtensionNplusOne = rule.extensionFormat;

View file

@ -545,11 +545,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
// and testing intersection there.
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) {
if (!_calculatedMeshBoxesValid) {
recalculateMeshBoxes(pickAgainstTriangles);
}
float bestDistance = std::numeric_limits<float>::max();
float distanceToSubMesh;
@ -560,6 +555,11 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
// If we hit the models box, then consider the submeshes...
_mutex.lock();
if (!_calculatedMeshBoxesValid) {
recalculateMeshBoxes(pickAgainstTriangles);
}
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) {
@ -1811,7 +1811,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
// We need to make sure we have valid offsets calculated before we can render
if (!_calculatedMeshPartOffsetValid) {
_mutex.lock();
recalculateMeshPartOffsets();
_mutex.unlock();
}
auto textureCache = DependencyManager::get<TextureCache>();
@ -2019,7 +2021,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
}
}
_mutex.lock();
qint64 offset = _calculatedMeshPartOffset[QPair<int,int>(meshIndex, partIndex)];
_mutex.unlock();
if (part.quadIndices.size() > 0) {
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);

View file

@ -46,24 +46,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi
AudioInjectorOptions optionsCopy = injectorOptions;
optionsCopy.stereo = sound->isStereo();
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
injector->setLocalAudioInterface(_localAudioInterface);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
return new ScriptAudioInjector(injector);
return new ScriptAudioInjector(AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface));
} else {
qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object.";

View file

@ -317,7 +317,10 @@ void ScriptEngine::init() {
registerAnimationTypes(this);
registerAvatarTypes(this);
registerAudioMetaTypes(this);
_controllerScriptingInterface->registerControllerTypes(this);
if (_controllerScriptingInterface) {
_controllerScriptingInterface->registerControllerTypes(this);
}
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);

View file

@ -20,6 +20,7 @@
static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
static int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>();
static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
@ -31,6 +32,7 @@ static int collisionMetaTypeId = qRegisterMetaType<Collision>();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue);
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue);
@ -93,6 +95,16 @@ QVector<glm::vec3> qVectorVec3FromScriptValue(const QScriptValue& array){
return newVector;
}
void qVectorVec3FromScriptValue(const QScriptValue& array, QVector<glm::vec3>& vector ) {
int length = array.property("length").toInteger();
for (int i = 0; i < length; i++) {
glm::vec3 newVec3 = glm::vec3();
vec3FromScriptValue(array.property(i), newVec3);
vector << newVec3;
}
}
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", vec2.x);

View file

@ -57,6 +57,7 @@ QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url);
void qURLFromScriptValue(const QScriptValue& object, QUrl& url);
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector);
void qVectorVec3FromScriptValue(const QScriptValue& array, QVector<glm::vec3>& vector);
QVector<glm::vec3> qVectorVec3FromScriptValue( const QScriptValue& array);
class PickRay {