Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-06-17 19:15:47 -07:00
commit c65af33a6c
13 changed files with 650 additions and 97 deletions

View file

@ -5,6 +5,19 @@
// Created by Clément Brisset on 4/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// This script allows you to edit models either with the razor hydras or with your mouse
//
// If using the hydras :
// grab grab models with the triggers, you can then move the models around or scale them with both hands.
// You can switch mode using the bumpers so that you can move models roud more easily.
//
// If using the mouse :
// - left click lets you move the model in the plane facing you.
// If pressing shift, it will move on the horizontale plane it's in.
// - right click lets you rotate the model. z and x give you access to more axix of rotation while shift allows for finer control.
// - left + right click lets you scale the model.
// - you can press r while holding the model to reset its rotation
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
@ -21,6 +34,8 @@ var LASER_COLOR = { red: 255, green: 0, blue: 0 };
var LASER_LENGTH_FACTOR = 500
;
var MAX_ANGULAR_SIZE = 45;
var LEFT = 0;
var RIGHT = 1;
@ -263,6 +278,11 @@ function controller(wichSide) {
var d = Vec3.length(Vec3.subtract(P, X));
if (0 < x && x < LASER_LENGTH_FACTOR) {
if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 > MAX_ANGULAR_SIZE) {
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
return { valid: false };
}
return { valid: true, x: x, y: y, z: z };
}
return { valid: false };
@ -513,9 +533,13 @@ function controller(wichSide) {
if (isLocked(newProperties)) {
print("Model locked " + newProperties.id);
} else {
var check = this.checkModel(newProperties);
if (!check.valid) {
return;
}
this.grab(newModel, newProperties);
var check = this.checkModel(newProperties);
this.x = check.x;
this.y = check.y;
this.z = check.z;
@ -653,16 +677,18 @@ function initToolBar() {
}
function moveOverlays() {
var newViewPort = Controller.getViewportDimensions();
if (typeof(toolBar) === 'undefined') {
initToolBar();
} else if (windowDimensions.x == Controller.getViewportDimensions().x &&
windowDimensions.y == Controller.getViewportDimensions().y) {
} else if (windowDimensions.x == newViewPort.x &&
windowDimensions.y == newViewPort.y) {
return;
}
windowDimensions = Controller.getViewportDimensions();
windowDimensions = newViewPort;
var toolsX = windowDimensions.x - 8 - toolBar.width;
var toolsY = (windowDimensions.y - toolBar.height) / 2;
@ -680,8 +706,6 @@ var intersection;
var SCALE_FACTOR = 200.0;
var TRANSLATION_FACTOR = 100.0;
var ROTATION_FACTOR = 100.0;
function rayPlaneIntersection(pickRay, point, normal) {
var d = -Vec3.dot(point, normal);
@ -690,8 +714,50 @@ function rayPlaneIntersection(pickRay, point, normal) {
return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t));
}
function Tooltip() {
this.x = 285;
this.y = 115;
this.width = 110;
this.height = 115 ;
this.margin = 5;
this.decimals = 3;
this.textOverlay = Overlays.addOverlay("text", {
x: this.x,
y: this.y,
width: this.width,
height: this.height,
margin: this.margin,
text: "",
color: { red: 128, green: 128, blue: 128 },
alpha: 0.2,
visible: false
});
this.show = function(doShow) {
Overlays.editOverlay(this.textOverlay, { visible: doShow });
}
this.updateText = function(properties) {
var angles = Quat.safeEulerAngles(properties.modelRotation);
var text = "Model Properties:\n"
text += "x: " + properties.position.x.toFixed(this.decimals) + "\n"
text += "y: " + properties.position.y.toFixed(this.decimals) + "\n"
text += "z: " + properties.position.z.toFixed(this.decimals) + "\n"
text += "pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "roll: " + angles.z.toFixed(this.decimals) + "\n"
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
Overlays.editOverlay(this.textOverlay, { text: text });
}
this.cleanup = function() {
Overlays.deleteOverlay(this.textOverlay);
}
}
var tooltip = new Tooltip();
function mousePressEvent(event) {
if (altIsPressed) {
if (event.isAlt) {
return;
}
@ -760,12 +826,16 @@ function mousePressEvent(event) {
var d = Vec3.length(Vec3.subtract(P, X));
if (0 < x && x < LASER_LENGTH_FACTOR) {
modelSelected = true;
selectedModelID = foundModel;
selectedModelProperties = properties;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 < MAX_ANGULAR_SIZE) {
modelSelected = true;
selectedModelID = foundModel;
selectedModelProperties = properties;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
} else {
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
}
}
}
}
@ -786,6 +856,8 @@ function mousePressEvent(event) {
selectedModelProperties.glowLevel = 0.0;
print("Clicked on " + selectedModelID.id + " " + modelSelected);
tooltip.updateText(selectedModelProperties);
tooltip.show(true);
}
}
@ -794,7 +866,7 @@ var oldModifier = 0;
var modifier = 0;
var wasShifted = false;
function mouseMoveEvent(event) {
if (altIsPressed) {
if (event.isAlt) {
return;
}
@ -886,26 +958,54 @@ function mouseMoveEvent(event) {
break;
case 3:
// Let's rotate
var rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: event.x - mouseLastPosition.x, z: 0 });
if (event.isShifted) {
rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: 0, z: mouseLastPosition.x - event.x });
if (somethingChanged) {
selectedModelProperties.oldRotation.x = selectedModelProperties.modelRotation.x;
selectedModelProperties.oldRotation.y = selectedModelProperties.modelRotation.y;
selectedModelProperties.oldRotation.z = selectedModelProperties.modelRotation.z;
selectedModelProperties.oldRotation.w = selectedModelProperties.modelRotation.w;
mouseLastPosition.x = event.x;
mouseLastPosition.y = event.y;
somethingChanged = false;
}
var newRotation = Quat.multiply(orientation, rotation);
newRotation = Quat.multiply(newRotation, Quat.inverse(orientation));
selectedModelProperties.modelRotation = Quat.multiply(newRotation, selectedModelProperties.oldRotation);
var pixelPerDegrees = windowDimensions.y / (1 * 360); // the entire height of the window allow you to make 2 full rotations
var STEP = 15;
var delta = Math.floor((event.x - mouseLastPosition.x) / pixelPerDegrees);
if (!event.isShifted) {
delta = Math.floor(delta / STEP) * STEP;
}
var rotation = Quat.fromVec3Degrees({
x: (!zIsPressed && xIsPressed) ? delta : 0, // z is pressed
y: (!zIsPressed && !xIsPressed) ? delta : 0, // x is pressed
z: (zIsPressed && !xIsPressed) ? delta : 0 // neither is pressed
});
rotation = Quat.multiply(selectedModelProperties.oldRotation, rotation);
selectedModelProperties.modelRotation.x = rotation.x;
selectedModelProperties.modelRotation.y = rotation.y;
selectedModelProperties.modelRotation.z = rotation.z;
selectedModelProperties.modelRotation.w = rotation.w;
break;
}
Models.editModel(selectedModelID, selectedModelProperties);
tooltip.updateText(selectedModelProperties);
}
function mouseReleaseEvent(event) {
if (altIsPressed) {
if (event.isAlt) {
return;
}
if (modelSelected) {
tooltip.show(false);
}
modelSelected = false;
glowedModelID.id = -1;
@ -943,6 +1043,7 @@ function scriptEnding() {
rightController.cleanup();
toolBar.cleanup();
cleanupModelMenus();
tooltip.cleanup();
}
Script.scriptEnding.connect(scriptEnding);
@ -974,16 +1075,36 @@ Menu.menuItemEvent.connect(function(menuItem){
}
});
// handling of inspect.js concurrence
altIsPressed = false;
var zIsPressed = false;
var xIsPressed = false;
var somethingChanged = false;
Controller.keyPressEvent.connect(function(event) {
if (event.text == "ALT") {
altIsPressed = true;
if ((event.text == "z" || event.text == "Z") && !zIsPressed) {
zIsPressed = true;
somethingChanged = true;
}
if ((event.text == "x" || event.text == "X") && !xIsPressed) {
xIsPressed = true;
somethingChanged = true;
}
// resets model orientation when holding with mouse
if (event.text == "r" && modelSelected) {
selectedModelProperties.modelRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 });
Models.editModel(selectedModelID, selectedModelProperties);
tooltip.updateText(selectedModelProperties);
somethingChanged = true;
}
});
Controller.keyReleaseEvent.connect(function(event) {
if (event.text == "ALT") {
altIsPressed = false;
if (event.text == "z" || event.text == "Z") {
zIsPressed = false;
somethingChanged = true;
}
});
if (event.text == "x" || event.text == "X") {
xIsPressed = false;
somethingChanged = true;
}
});

119
examples/myBalance.js Normal file
View file

@ -0,0 +1,119 @@
//
// myBalance.js
// examples
//
// Created by Stojce Slavkovski on June 5, 2014
// Copyright 2014 High Fidelity, Inc.
//
// Show wallet ₵ balance
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var Controller = Controller || {};
var Overlays = Overlays || {};
var Script = Script || {};
var Account = Account || {};
(function () {
"use strict";
var iconUrl = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/',
overlayWidth = 150,
overlayHeight = 50,
overlayTopOffset = 15,
overlayRightOffset = 140,
textRightOffset = 105,
maxIntegers = 5,
downColor = {
red: 0,
green: 0,
blue: 255
},
upColor = {
red: 0,
green: 255,
blue: 0
},
normalColor = {
red: 204,
green: 204,
blue: 204
},
balance = -1,
walletBox = Overlays.addOverlay("image", {
x: 0,
y: overlayTopOffset,
width: 122,
height: 32,
imageURL: iconUrl + "wallet.svg",
alpha: 1
}),
textOverlay = Overlays.addOverlay("text", {
x: 0,
y: overlayTopOffset,
topMargin: 10,
font: {
size: 16
},
color: normalColor
});
function scriptEnding() {
Overlays.deleteOverlay(walletBox);
Overlays.deleteOverlay(textOverlay);
}
function update(deltaTime) {
var xPos = Controller.getViewportDimensions().x;
Overlays.editOverlay(walletBox, {
x: xPos - overlayRightOffset,
visible: Account.isLoggedIn()
});
Overlays.editOverlay(textOverlay, {
x: xPos - textRightOffset,
visible: Account.isLoggedIn()
});
}
function formatedBalance() {
var integers = balance.toFixed(0).length,
decimals = Math.abs(maxIntegers - integers) + 2;
var x = balance.toFixed(decimals).split('.'),
x1 = x[0],
x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
function updateBalance(newBalance) {
if (balance === newBalance) {
return;
}
var change = newBalance - balance,
textColor = change < 0 ? downColor : upColor;
balance = newBalance;
Overlays.editOverlay(textOverlay, {
text: formatedBalance(),
color: textColor
});
Script.setTimeout(function () {
Overlays.editOverlay(textOverlay, {
color: normalColor
});
}, 1000);
}
updateBalance(Account.getBalance());
Account.balanceChanged.connect(updateBalance);
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);
}());

View file

@ -73,6 +73,7 @@
#include "devices/TV3DManager.h"
#include "renderer/ProgramObject.h"
#include "scripting/AccountScriptingInterface.h"
#include "scripting/AudioDeviceScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
@ -654,7 +655,14 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("paintGL/renderOverlay");
_applicationOverlay.renderOverlay();
//If alpha is 1, we can render directly to the screen.
if (_applicationOverlay.getAlpha() == 1.0f) {
_applicationOverlay.renderOverlay();
} else {
//Render to to texture so we can fade it
_applicationOverlay.renderOverlay(true);
_applicationOverlay.displayOverlayTexture();
}
}
}
@ -3572,6 +3580,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
QThread* workerThread = new QThread(this);

View file

@ -341,8 +341,9 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
//Injecting mouse movements and clicks
void SixenseManager::emulateMouse(PalmData* palm, int index) {
MyAvatar* avatar = Application::getInstance()->getAvatar();
QGLWidget* widget = Application::getInstance()->getGLWidget();
Application* application = Application::getInstance();
MyAvatar* avatar = application->getAvatar();
QGLWidget* widget = application->getGLWidget();
QPoint pos;
// Get directon relative to avatar orientation
glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
@ -374,14 +375,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
if (_bumperPressed[index]) {
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0);
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
application->mouseReleaseEvent(&mouseEvent);
_bumperPressed[index] = false;
}
if (_triggerPressed[index]) {
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0);
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
application->mouseReleaseEvent(&mouseEvent);
_triggerPressed[index] = false;
}
@ -396,17 +397,28 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
//This is specifically for edit voxels
if (triggerButton == Qt::LeftButton) {
if (!_triggerPressed[(int)(!index)]) {
Application::getInstance()->mouseMoveEvent(&mouseEvent);
application->mouseMoveEvent(&mouseEvent);
}
} else {
if (!_bumperPressed[(int)(!index)]) {
Application::getInstance()->mouseMoveEvent(&mouseEvent);
application->mouseMoveEvent(&mouseEvent);
}
}
}
_oldX[index] = pos.x();
_oldY[index] = pos.y();
//We need separate coordinates for clicks, since we need to check if
//a magnification window was clicked on
int clickX = pos.x();
int clickY = pos.y();
//Checks for magnification window click
application->getApplicationOverlay().getClickLocation(clickX, clickY);
//Set pos to the new click location, which may be the same if no magnification window is open
pos.setX(clickX);
pos.setY(clickY);
//Check for bumper press ( Right Click )
if (palm->getControllerButtons() & BUTTON_FWD) {
if (!_bumperPressed[index]) {
@ -414,12 +426,12 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0);
Application::getInstance()->mousePressEvent(&mouseEvent);
application->mousePressEvent(&mouseEvent);
}
} else if (_bumperPressed[index]) {
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0);
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
application->mouseReleaseEvent(&mouseEvent);
_bumperPressed[index] = false;
}
@ -431,12 +443,12 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0);
Application::getInstance()->mousePressEvent(&mouseEvent);
application->mousePressEvent(&mouseEvent);
}
} else if (_triggerPressed[index]) {
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0);
Application::getInstance()->mouseReleaseEvent(&mouseEvent);
application->mouseReleaseEvent(&mouseEvent);
_triggerPressed[index] = false;
}

View file

@ -0,0 +1,41 @@
//
// AccountScriptingInterface.cpp
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AccountManager.h"
#include "AccountScriptingInterface.h"
AccountScriptingInterface::AccountScriptingInterface() {
AccountManager& accountManager = AccountManager::getInstance();
connect(&accountManager, &AccountManager::balanceChanged, this,
&AccountScriptingInterface::updateBalance);
}
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
return &sharedInstance;
}
float AccountScriptingInterface::getBalance() {
AccountManager& accountManager = AccountManager::getInstance();
return accountManager.getAccountInfo().getBalanceInSatoshis();
}
bool AccountScriptingInterface::isLoggedIn() {
AccountManager& accountManager = AccountManager::getInstance();
return accountManager.isLoggedIn();
}
void AccountScriptingInterface::updateBalance() {
AccountManager& accountManager = AccountManager::getInstance();
emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis());
}

View file

@ -0,0 +1,31 @@
//
// AccountScriptingInterface.h
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AccountScriptingInterface_h
#define hifi_AccountScriptingInterface_h
#include <QObject>
class AccountScriptingInterface : public QObject {
Q_OBJECT
AccountScriptingInterface();
signals:
void balanceChanged(float newBalance);
public slots:
static AccountScriptingInterface* getInstance();
float getBalance();
bool isLoggedIn();
void updateBalance();
};
#endif // hifi_AccountScriptingInterface_h

View file

@ -20,6 +20,13 @@
#include "ui/Stats.h"
// Used to fade the UI
const float FADE_SPEED = 0.08f;
// Used to animate the magnification windows
const float MAG_SPEED = 0.08f;
const quint64 MSECS_TO_USECS = 1000ULL;
// Fast helper functions
inline float max(float a, float b) {
return (a > b) ? a : b;
@ -31,11 +38,14 @@ inline float min(float a, float b) {
ApplicationOverlay::ApplicationOverlay() :
_framebufferObject(NULL),
_oculusAngle(65.0f * RADIANS_PER_DEGREE),
_distance(0.5f),
_textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE),
_crosshairTexture(0) {
_crosshairTexture(0),
_alpha(1.0f),
_active(true) {
memset(_reticleActive, 0, sizeof(_reticleActive));
memset(_magActive, 0, sizeof(_reticleActive));
memset(_magSizeMult, 0, sizeof(_magSizeMult));
}
ApplicationOverlay::~ApplicationOverlay() {
@ -45,11 +55,11 @@ ApplicationOverlay::~ApplicationOverlay() {
}
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
// Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay(bool renderToTexture) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
_textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE;
@ -60,6 +70,19 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
QGLWidget* glWidget = application->getGLWidget();
MyAvatar* myAvatar = application->getAvatar();
//Handle fadeing and deactivation/activation of UI
if (_active) {
_alpha += FADE_SPEED;
if (_alpha > 1.0f) {
_alpha = 1.0f;
}
} else {
_alpha -= FADE_SPEED;
if (_alpha <= 0.0f) {
_alpha = 0.0f;
}
}
if (renderToTexture) {
getFramebufferObject()->bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -67,6 +90,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Render 2D overlay
glMatrixMode(GL_PROJECTION);
glPushMatrix();
@ -93,6 +117,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
@ -104,7 +129,11 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
}
// Draws the FBO texture for the screen
void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) {
void ApplicationOverlay::displayOverlayTexture() {
if (_alpha == 0.0f) {
return;
}
Application* application = Application::getInstance();
QGLWidget* glWidget = application->getGLWidget();
@ -120,13 +149,16 @@ void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) {
gluOrtho2D(0, glWidget->width(), glWidget->height(), 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBegin(GL_QUADS);
glColor4f(1.0f, 1.0f, 1.0f, _alpha);
glTexCoord2f(0, 0); glVertex2i(0, glWidget->height());
glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height());
glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0);
glTexCoord2f(0, 1); glVertex2i(0, 0);
glEnd();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
@ -149,15 +181,42 @@ void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direc
direction = glm::normalize(rot * glm::vec3(x, y, z));
}
// Calculates the click location on the screen by taking into account any
// opened magnification windows.
void ApplicationOverlay::getClickLocation(int &x, int &y) const {
int dx;
int dy;
const float xRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f;
const float yRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f;
//Loop through all magnification windows
for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) {
if (_magActive[i]) {
dx = x - _magX[i];
dy = y - _magY[i];
//Check to see if they clicked inside a mag window
if (abs(dx) <= xRange && abs(dy) <= yRange) {
//Move the click to the actual UI location by inverting the magnification
x = dx / MAGNIFY_MULT + _magX[i];
y = dy / MAGNIFY_MULT + _magY[i];
return;
}
}
}
}
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane.
void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
if (_alpha == 0.0f) {
return;
}
Application* application = Application::getInstance();
MyAvatar* myAvatar = application->getAvatar();
const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
@ -185,21 +244,37 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
glTranslatef(pos.x, pos.y, pos.z);
glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_TRUE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.01f);
//Draw the magnifiers
for (int i = 0; i < _numMagnifiers; i++) {
renderMagnifier(_mouseX[i], _mouseY[i]);
//Update and draw the magnifiers
for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) {
if (_magActive[i]) {
_magSizeMult[i] += MAG_SPEED;
if (_magSizeMult[i] > 1.0f) {
_magSizeMult[i] = 1.0f;
}
} else {
_magSizeMult[i] -= MAG_SPEED;
if (_magSizeMult[i] < 0.0f) {
_magSizeMult[i] = 0.0f;
}
}
if (_magSizeMult[i] > 0.0f) {
//Render magnifier, but dont show border for mouse magnifier
renderMagnifier(_magX[i], _magY[i], _magSizeMult[i], i != MOUSE);
}
}
glDepthMask(GL_FALSE);
glDisable(GL_ALPHA_TEST);
glColor4f(1.0f, 1.0f, 1.0f, _alpha);
renderTexturedHemisphere();
renderControllerPointersOculus();
@ -213,13 +288,13 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_LIGHTING);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
//Renders optional pointers
void ApplicationOverlay::renderPointers() {
Application* application = Application::getInstance();
// Render a crosshair over the mouse when in Oculus
_numMagnifiers = 0;
//lazily load crosshair texture
if (_crosshairTexture == 0) {
@ -233,11 +308,20 @@ void ApplicationOverlay::renderPointers() {
if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) {
//If we are in oculus, render reticle later
_numMagnifiers = 1;
_mouseX[0] = application->getMouseX();
_mouseY[0] = application->getMouseY();
_reticleActive[MOUSE] = true;
_magActive[MOUSE] = true;
_mouseX[MOUSE] = application->getMouseX();
_mouseY[MOUSE] = application->getMouseY();
_magX[MOUSE] = _mouseX[MOUSE];
_magY[MOUSE] = _mouseY[MOUSE];
_reticleActive[LEFT_CONTROLLER] = false;
_reticleActive[RIGHT_CONTROLLER] = false;
} else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
//only render controller pointer if we aren't already rendering a mouse pointer
_reticleActive[MOUSE] = false;
_magActive[MOUSE] = false;
renderControllerPointers();
}
glBindTexture(GL_TEXTURE_2D, 0);
@ -250,9 +334,18 @@ void ApplicationOverlay::renderControllerPointers() {
QGLWidget* glWidget = application->getGLWidget();
MyAvatar* myAvatar = application->getAvatar();
//Static variables used for storing controller state
static quint64 pressedTime[NUMBER_OF_MAGNIFIERS] = { 0ULL, 0ULL, 0ULL };
static bool isPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool stateWhenPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool triggerPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
static bool bumperPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false };
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
const int index = palmIndex - 1;
const PalmData* palmData = NULL;
if (palmIndex >= handData->getPalms().size()) {
@ -265,6 +358,51 @@ void ApplicationOverlay::renderControllerPointers() {
continue;
}
int controllerButtons = palmData->getControllerButtons();
//Check for if we should toggle or drag the magnification window
if (controllerButtons & BUTTON_3) {
if (isPressed[index] == false) {
//We are now dragging the window
isPressed[index] = true;
//set the pressed time in us
pressedTime[index] = usecTimestampNow();
stateWhenPressed[index] = _magActive[index];
}
} else if (isPressed[index]) {
isPressed[index] = false;
//If the button was only pressed for < 250 ms
//then disable it.
const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS;
if (usecTimestampNow() - pressedTime[index] < MAX_BUTTON_PRESS_TIME) {
_magActive[index] = !stateWhenPressed[index];
}
}
//Check for UI active toggle
if (palmData->getTrigger() == 1.0f) {
if (!triggerPressed[index]) {
if (bumperPressed[index]) {
_active = !_active;
}
triggerPressed[index] = true;
}
} else {
triggerPressed[index] = false;
}
if ((controllerButtons & BUTTON_FWD)) {
if (!bumperPressed[index]) {
if (triggerPressed[index]) {
_active = !_active;
}
bumperPressed[index] = true;
}
} else {
bumperPressed[index] = false;
}
// Get directon relative to avatar orientation
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
@ -280,34 +418,42 @@ void ApplicationOverlay::renderControllerPointers() {
//If the cursor is out of the screen then don't render it
if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) {
_reticleActive[index] = false;
continue;
}
float pointerWidth = 40;
float pointerHeight = 40;
_reticleActive[index] = true;
//if we have the oculus, we should make the cursor smaller since it will be
//magnified
if (OculusManager::isConnected()) {
_mouseX[index] = mouseX;
_mouseY[index] = mouseY;
_mouseX[_numMagnifiers] = mouseX;
_mouseY[_numMagnifiers] = mouseY;
_numMagnifiers++;
//If oculus is enabled, we draw the crosshairs later
//When button 2 is pressed we drag the mag window
if (isPressed[index]) {
_magActive[index] = true;
_magX[index] = mouseX;
_magY[index] = mouseY;
}
// If oculus is enabled, we draw the crosshairs later
continue;
}
mouseX -= pointerWidth / 2.0f;
mouseY += pointerHeight / 2.0f;
const float reticleSize = 40.0f;
mouseX -= reticleSize / 2.0f;
mouseY += reticleSize / 2.0f;
glBegin(GL_QUADS);
glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]);
glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY);
glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY);
glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight);
glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight);
glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + reticleSize, mouseY);
glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + reticleSize, mouseY - reticleSize);
glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - reticleSize);
glEnd();
}
@ -325,7 +471,12 @@ void ApplicationOverlay::renderControllerPointersOculus() {
glBindTexture(GL_TEXTURE_2D, _crosshairTexture);
glDisable(GL_DEPTH_TEST);
for (int i = 0; i < _numMagnifiers; i++) {
for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) {
//Dont render the reticle if its inactive
if (!_reticleActive[i]) {
continue;
}
float mouseX = (float)_mouseX[i];
float mouseY = (float)_mouseY[i];
@ -360,7 +511,7 @@ void ApplicationOverlay::renderControllerPointersOculus() {
glBegin(GL_QUADS);
glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]);
glColor4f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], _alpha);
glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ);
glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ);
@ -374,24 +525,22 @@ void ApplicationOverlay::renderControllerPointersOculus() {
}
//Renders a small magnification of the currently bound texture at the coordinates
void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY)
void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult, bool showBorder) const
{
Application* application = Application::getInstance();
QGLWidget* glWidget = application->getGLWidget();
const int widgetWidth = glWidget->width();
const int widgetHeight = glWidget->height();
const float magnification = 4.0f;
float magnifyWidth = 80.0f;
float magnifyHeight = 60.0f;
const float magnifyWidth = MAGNIFY_WIDTH * sizeMult;
const float magnifyHeight = MAGNIFY_HEIGHT * sizeMult;
mouseX -= magnifyWidth / 2;
mouseY -= magnifyHeight / 2;
float newWidth = magnifyWidth * magnification;
float newHeight = magnifyHeight * magnification;
float newWidth = magnifyWidth * MAGNIFY_MULT;
float newHeight = magnifyHeight * MAGNIFY_MULT;
// Magnification Texture Coordinates
float magnifyULeft = mouseX / (float)widgetWidth;
@ -417,19 +566,60 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY)
float bY = sin((newVBottom - 0.5f) * _textureFov);
float tY = sin((newVTop - 0.5f) * _textureFov);
float blZ, tlZ, brZ, trZ;
float dist;
float discriminant;
//Bottom Left
dist = sqrt(lX * lX + bY * bY);
float blZ = sqrt(1.0f - dist * dist);
discriminant = 1.0f - dist * dist;
if (discriminant > 0) {
blZ = sqrt(discriminant);
} else {
blZ = 0;
}
//Top Left
dist = sqrt(lX * lX + tY * tY);
float tlZ = sqrt(1.0f - dist * dist);
discriminant = 1.0f - dist * dist;
if (discriminant > 0) {
tlZ = sqrt(discriminant);
} else {
tlZ = 0;
}
//Bottom Right
dist = sqrt(rX * rX + bY * bY);
float brZ = sqrt(1.0f - dist * dist);
discriminant = 1.0f - dist * dist;
if (discriminant > 0) {
brZ = sqrt(discriminant);
} else {
brZ = 0;
}
//Top Right
dist = sqrt(rX * rX + tY * tY);
float trZ = sqrt(1.0f - dist * dist);
discriminant = 1.0f - dist * dist;
if (discriminant > 0) {
trZ = sqrt(discriminant);
} else {
trZ = 0;
}
if (showBorder) {
glDisable(GL_TEXTURE_2D);
glLineWidth(1.0f);
//Outer Line
glBegin(GL_LINE_STRIP);
glColor4f(1.0f, 0.0f, 0.0f, _alpha);
glVertex3f(lX, tY, -tlZ);
glVertex3f(rX, tY, -trZ);
glVertex3f(rX, bY, -brZ);
glVertex3f(lX, bY, -blZ);
glVertex3f(lX, tY, -tlZ);
glEnd();
glEnable(GL_TEXTURE_2D);
}
glColor4f(1.0f, 1.0f, 1.0f, _alpha);
glBegin(GL_QUADS);

View file

@ -15,6 +15,10 @@
class Overlays;
class QOpenGLFramebufferObject;
const float MAGNIFY_WIDTH = 160.0f;
const float MAGNIFY_HEIGHT = 80.0f;
const float MAGNIFY_MULT = 4.0f;
// Handles the drawing of the overlays to the screen
class ApplicationOverlay {
public:
@ -23,12 +27,14 @@ public:
~ApplicationOverlay();
void renderOverlay(bool renderToTexture = false);
void displayOverlayTexture(Camera& whichCamera);
void displayOverlayTexture();
void displayOverlayTextureOculus(Camera& whichCamera);
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
void getClickLocation(int &x, int &y) const;
// Getters
QOpenGLFramebufferObject* getFramebufferObject();
float getAlpha() const { return _alpha; }
private:
// Interleaved vertex data
@ -42,20 +48,27 @@ private:
void renderPointers();
void renderControllerPointers();
void renderControllerPointersOculus();
void renderMagnifier(int mouseX, int mouseY);
void renderMagnifier(int mouseX, int mouseY, float sizeMult, bool showBorder) const;
void renderAudioMeter();
void renderStatsAndLogs();
void renderTexturedHemisphere();
QOpenGLFramebufferObject* _framebufferObject;
float _trailingAudioLoudness;
float _oculusAngle;
float _distance;
float _textureFov;
int _mouseX[2];
int _mouseY[2];
int _numMagnifiers;
enum MagnifyDevices { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_MAGNIFIERS = RIGHT_CONTROLLER + 1 };
bool _reticleActive[NUMBER_OF_MAGNIFIERS];
int _mouseX[NUMBER_OF_MAGNIFIERS];
int _mouseY[NUMBER_OF_MAGNIFIERS];
bool _magActive[NUMBER_OF_MAGNIFIERS];
int _magX[NUMBER_OF_MAGNIFIERS];
int _magY[NUMBER_OF_MAGNIFIERS];
float _magSizeMult[NUMBER_OF_MAGNIFIERS];
float _alpha;
bool _active;
GLuint _crosshairTexture;
};

View file

@ -26,9 +26,9 @@ Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int wid
}
TextRenderer::TextRenderer(const char* family, int pointSize, int weight,
bool italic, EffectType effectType, int effectThickness)
bool italic, EffectType effectType, int effectThickness, QColor color)
: _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType),
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) {
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), _color(color) {
_font.setKerning(false);
}
@ -157,7 +157,7 @@ const Glyph& TextRenderer::getGlyph(char c) {
// render the glyph into an image and copy it into the texture
QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32);
if (c == SOLID_BLOCK_CHAR) {
image.fill(QColor(255, 255, 255));
image.fill(_color);
} else {
image.fill(0);
@ -180,7 +180,7 @@ const Glyph& TextRenderer::getGlyph(char c) {
painter.setRenderHint(QPainter::Antialiasing);
painter.drawPath(path);
}
painter.setPen(QColor(255, 255, 255));
painter.setPen(_color);
painter.drawText(-bounds.x(), -bounds.y(), ch);
}
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());

View file

@ -12,6 +12,7 @@
#ifndef hifi_TextRenderer_h
#define hifi_TextRenderer_h
#include <QColor>
#include <QFont>
#include <QFontMetrics>
#include <QHash>
@ -41,7 +42,8 @@ public:
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
EffectType effect = NO_EFFECT, int effectThickness = 1);
EffectType effect = NO_EFFECT, int effectThickness = 1,
QColor color = QColor(255, 255, 255));
~TextRenderer();
const QFontMetrics& metrics() const { return _metrics; }
@ -85,6 +87,9 @@ private:
// the list of all texture ids for which we're responsible
QVector<GLuint> _allTextureIDs;
// text color
QColor _color;
};
class Glyph {

View file

@ -19,7 +19,8 @@
TextOverlay::TextOverlay() :
_leftMargin(DEFAULT_MARGIN),
_topMargin(DEFAULT_MARGIN)
_topMargin(DEFAULT_MARGIN),
_fontSize(DEFAULT_FONTSIZE)
{
}
@ -32,7 +33,7 @@ void TextOverlay::render() {
}
const float MAX_COLOR = 255;
glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha);
glColor4f(0 / MAX_COLOR, 0 / MAX_COLOR, 0 / MAX_COLOR, _alpha);
glBegin(GL_QUADS);
glVertex2f(_bounds.left(), _bounds.top());
@ -43,8 +44,9 @@ void TextOverlay::render() {
//TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
// EffectType effect = NO_EFFECT, int effectThickness = 1);
TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50);
TextRenderer textRenderer(SANS_FONT_FAMILY, _fontSize, 50, false, TextRenderer::NO_EFFECT, 1,
QColor(_color.red, _color.green, _color.blue));
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
const int topAdjust = -2; // required to make text render relative to top edge of bounds
int x = _bounds.left() + _leftMargin + leftAdjust;
@ -67,6 +69,13 @@ void TextOverlay::render() {
void TextOverlay::setProperties(const QScriptValue& properties) {
Overlay2D::setProperties(properties);
QScriptValue font = properties.property("font");
if (font.isObject()) {
if (font.property("size").isValid()) {
setFontSize(font.property("size").toInt32());
}
}
QScriptValue text = properties.property("text");
if (text.isValid()) {

View file

@ -29,6 +29,7 @@
#include "Overlay2D.h"
const int DEFAULT_MARGIN = 10;
const int DEFAULT_FONTSIZE = 11;
class TextOverlay : public Overlay2D {
Q_OBJECT
@ -47,6 +48,7 @@ public:
void setText(const QString& text) { _text = text; }
void setLeftMargin(int margin) { _leftMargin = margin; }
void setTopMargin(int margin) { _topMargin = margin; }
void setFontSize(int fontSize) { _fontSize = fontSize; }
virtual void setProperties(const QScriptValue& properties);
@ -55,7 +57,7 @@ private:
QString _text;
int _leftMargin;
int _topMargin;
int _fontSize;
};

View file

@ -38,6 +38,7 @@ public:
void setDiscourseApiKey(const QString& discourseApiKey);
qint64 getBalance() const { return _balance; }
float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; }
void setBalance(qint64 balance);
bool hasBalance() const { return _hasBalance; }
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }