Merge branch master of upstream

This commit is contained in:
Mohammed Nafees 2014-05-29 22:31:32 +05:30
commit 2c3784c7d5
28 changed files with 1664 additions and 350 deletions

View file

@ -312,6 +312,8 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr
int numInstances = jsonObject[ASSIGNMENT_INSTANCES_KEY].toInt();
numInstances = (numInstances == 0 ? 1 : numInstances);
qDebug() << "Adding a static scripted assignment from" << assignmentURL;
for (int i = 0; i < numInstances; i++) {
// add a scripted assignment to the queue for this instance
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand,
@ -319,13 +321,8 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr
assignmentPool);
scriptAssignment->setPayload(assignmentURL.toUtf8());
qDebug() << "Adding scripted assignment to queue -" << *scriptAssignment;
qDebug() << "URL for script is" << assignmentURL;
// scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies
SharedAssignmentPointer sharedScriptAssignment(scriptAssignment);
_unfulfilledAssignments.enqueue(sharedScriptAssignment);
_allAssignments.insert(sharedScriptAssignment->getUUID(), sharedScriptAssignment);
addStaticAssignmentToAssignmentHash(scriptAssignment);
}
}
}
@ -407,7 +404,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
PendingAssignedNodeData* pendingAssigneeData = NULL;
if (isAssignment) {
pendingAssigneeData = _pendingAssignedNodes.take(packetUUID);
pendingAssigneeData = _pendingAssignedNodes.value(packetUUID);
if (pendingAssigneeData) {
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType);
@ -416,12 +413,21 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID)
<< "matches unfulfilled assignment"
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
// remove this unique assignment deployment from the hash of pending assigned nodes
// cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList
_pendingAssignedNodes.remove(packetUUID);
} else {
// this is a node connecting to fulfill an assignment that doesn't exist
// don't reply back to them so they cycle back and re-request an assignment
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID);
return;
}
}
}
if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) {
if (!isAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) {
// this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones
if (_sessionAuthenticationHash.contains(packetUUID)) {
if (!_sessionAuthenticationHash.value(packetUUID)) {

View file

@ -31,12 +31,12 @@ var originalProperties = {
green: 255,
blue: 0 },
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo",
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/minotaur/mino_full.fbx",
modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
modelRotation: rotation
};

View file

@ -28,7 +28,7 @@ var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
var PIXELS_PER_EXTRUDE_VOXEL = 16;
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
var MAX_VOXEL_SCALE = 1.0;
var MAX_VOXEL_SCALE = 16.0;
var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0);
var WHITE_COLOR = { red: 255, green: 255, blue: 255 };
@ -394,6 +394,9 @@ function ScaleSelector() {
if (this.power < 13) {
++this.power;
this.scale *= 2.0;
if (this.scale > MAX_VOXEL_SCALE) {
this.scale = MAX_VOXEL_SCALE;
}
this.update();
rescaleImport();
resizeVoxelSound.play(voxelSizePlus);
@ -1056,6 +1059,9 @@ function mousePressEvent(event) {
lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z };
lastVoxelColor = { red: newColor.red, green: newColor.green, blue: newColor.blue };
lastVoxelScale = voxelDetails.s;
if (lastVoxelScale > MAX_VOXEL_SCALE) {
lastVoxelScale = MAX_VOXEL_SCALE;
}
addVoxelSound.playRandom();

302
examples/locationsMenu.js Normal file
View file

@ -0,0 +1,302 @@
//
// locationsMenu.js
// examples
//
// Created by Ryan Huffman on 5/28/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
//
var scriptUrl = "https://script.google.com/macros/s/AKfycbwIo4lmF-qUwX1Z-9eA_P-g2gse9oFhNcjVyyksGukyDDEFXgU/exec?action=listOwners&domain=alpha.highfidelity.io";
var LocationMenu = function(opts) {
var self = this;
var pageSize = opts.pageSize || 10;
var menuWidth = opts.menuWidth || 150;
var menuHeight = opts.menuItemHeight || 24;
var inactiveColor = { red: 51, green: 102, blue: 102 };
var activeColor = { red: 18, green: 66, blue: 66 };
var prevNextColor = { red: 192, green: 192, blue: 192 };
var disabledColor = { red: 64, green: 64, blue: 64};
var position = { x: 0, y: 0 };
var locationIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/location.svg";
var toolHeight = 50;
var toolWidth = 50;
var visible = false;
var menuItemOffset = {
x: 55,
y: 0,
};
var menuItemPadding = 5;
var margin = 7;
var fullMenuHeight = (2 * menuItemOffset.y) + (menuHeight * (pageSize + 1));
var menuOffset = -fullMenuHeight + toolHeight;
var windowDimensions = Controller.getViewportDimensions();
this.locations = [];
this.numPages = 1;
this.page = 0;
this.menuToggleButton = Overlays.addOverlay("image", {
x: position.x,
y: position.y,
width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: locationIconUrl,
alpha: 0.9
});
this.background = Overlays.addOverlay("text", {
x: 0,
y: 0,
width: menuWidth + 10,
height: (menuHeight * (pageSize + 1)) + 10,
color: { red: 0, green: 0, blue: 0},
topMargin: 4,
leftMargin: 4,
text: "",
visible: visible,
});
this.menuItems = [];
for (var i = 0; i < pageSize; i++) {
var menuItem = Overlays.addOverlay("text", {
x: 0,
y: 0,
width: menuWidth,
height: menuHeight,
color: inactiveColor,
topMargin: margin,
leftMargin: margin,
text: (i == 0) ? "Loading..." : "",
visible: visible,
});
this.menuItems.push({ overlay: menuItem, location: null });
}
this.previousButton = Overlays.addOverlay("text", {
x: 0,
y: 0,
width: menuWidth / 2,
height: menuHeight,
color: disabledColor,
topMargin: margin,
leftMargin: margin,
text: "Previous",
visible: visible,
});
this.nextButton = Overlays.addOverlay("text", {
x: 0,
y: 0,
width: menuWidth / 2,
height: menuHeight,
color: disabledColor,
topMargin: margin,
leftMargin: margin,
text: "Next",
visible: visible,
});
this.reposition = function(force) {
var newWindowDimensions = Controller.getViewportDimensions();
if (force || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
position.x = 8;
position.y = Math.floor(windowDimensions.y / 2) + 25 + 50 + 8;
Overlays.editOverlay(self.menuToggleButton, {
x: position.x,
y: position.y,
});
Overlays.editOverlay(self.background, {
x: position.x + menuItemOffset.x,
y: position.y + menuItemOffset.y - 2 * menuItemPadding + menuOffset,
});
for (var i = 0; i < pageSize; i++) {
Overlays.editOverlay(self.menuItems[i].overlay, {
x: position.x + menuItemOffset.x + menuItemPadding,
y: position.y + menuItemOffset.y - menuItemPadding + (i * menuHeight) + menuOffset,
});
}
Overlays.editOverlay(self.previousButton, {
x: position.x + menuItemOffset.x + menuItemPadding,
y: position.y + menuItemOffset.y - menuItemPadding + (pageSize * menuHeight) + menuOffset,
});
Overlays.editOverlay(self.nextButton, {
x: position.x + menuItemOffset.x + menuItemPadding + (menuWidth / 2),
y: position.y + menuItemOffset.y - menuItemPadding + (pageSize * menuHeight) + menuOffset,
});
}
}
this.updateLocations = function(locations) {
this.locations = locations;
this.numPages = Math.ceil(locations.length / pageSize);
this.goToPage(0);
}
this.setError = function() {
Overlays.editOverlay(this.menuItems[0].overlay, { text: "Error loading data" });
}
this.toggleMenu = function() {
visible = !visible;
for (var i = 0; i < this.menuItems.length; i++) {
Overlays.editOverlay(this.menuItems[i].overlay, { visible: visible});
}
Overlays.editOverlay(this.previousButton, { visible: visible});
Overlays.editOverlay(this.nextButton, { visible: visible});
Overlays.editOverlay(this.background, { visible: visible});
if (visible) {
Overlays.editOverlay(this.menuToggleButton, { subImage: { x: 0, y: 0, width: toolWidth, height: toolHeight } }),
} else {
Overlays.editOverlay(this.menuToggleButton, { subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight } }),
}
}
this.goToPage = function(pageNumber) {
if (pageNumber < 0 || pageNumber >= this.numPages) {
return;
}
this.page = pageNumber;
var start = pageNumber * pageSize;
for (var i = 0; i < pageSize; i++) {
var update = {};
var location = null;
if (start + i < this.locations.length) {
location = this.locations[start + i];
update.text = (start + i + 1) + ". " + location.username;
update.color = inactiveColor;
} else {
update.text = "";
update.color = disabledColor;
}
Overlays.editOverlay(this.menuItems[i].overlay, update);
this.menuItems[i].location = location;
}
this.previousEnabled = pageNumber > 0;
this.nextEnabled = pageNumber < (this.numPages - 1);
Overlays.editOverlay(this.previousButton, { color: this.previousEnabled ? prevNextColor : disabledColor});
Overlays.editOverlay(this.nextButton, { color: this.nextEnabled ? prevNextColor : disabledColor });
}
this.mousePressEvent = function(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (clickedOverlay == self.menuToggleButton) {
self.toggleMenu();
} else if (clickedOverlay == self.previousButton) {
if (self.previousEnabled) {
Overlays.editOverlay(clickedOverlay, { color: activeColor });
}
} else if (clickedOverlay == self.nextButton) {
if (self.nextEnabled) {
Overlays.editOverlay(clickedOverlay, { color: activeColor });
}
} else {
for (var i = 0; i < self.menuItems.length; i++) {
if (clickedOverlay == self.menuItems[i].overlay) {
if (self.menuItems[i].location != null) {
Overlays.editOverlay(clickedOverlay, { color: activeColor });
}
break;
}
}
}
}
this.mouseReleaseEvent = function(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (clickedOverlay == self.previousButton) {
if (self.previousEnabled) {
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
self.goToPage(self.page - 1);
}
} else if (clickedOverlay == self.nextButton) {
if (self.nextEnabled) {
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
self.goToPage(self.page + 1);
}
} else {
for (var i = 0; i < self.menuItems.length; i++) {
if (clickedOverlay == self.menuItems[i].overlay) {
if (self.menuItems[i].location != null) {
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
var location = self.menuItems[i].location;
Window.location = "hifi://" + location.domain + "/"
+ location.x + "," + location.y + "," + location.z;
}
break;
}
}
}
}
this.cleanup = function() {
for (var i = 0; i < self.menuItems.length; i++) {
Overlays.deleteOverlay(self.menuItems[i].overlay);
}
Overlays.deleteOverlay(self.menuToggleButton);
Overlays.deleteOverlay(self.previousButton);
Overlays.deleteOverlay(self.nextButton);
Overlays.deleteOverlay(self.background);
}
Controller.mousePressEvent.connect(this.mousePressEvent);
Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent);
Script.update.connect(this.reposition);
Script.scriptEnding.connect(this.cleanup);
this.reposition(true);
};
var locationMenu = new LocationMenu({ pageSize: 8 });
print("Loading strip data from " + scriptUrl);
var req = new XMLHttpRequest();
req.responseType = 'json';
req.onreadystatechange = function() {
if (req.readyState == req.DONE) {
if (req.status == 200 && req.response != null) {
for (var domain in req.response) {
var locations = req.response[domain];
var users = [];
for (var i = 0; i < locations.length; i++) {
var loc = locations[i];
var x1 = loc[1],
x2 = loc[2],
y1 = loc[3],
y2 = loc[4];
users.push({
domain: domain,
username: loc[0],
x: x1,
y: 300,
z: y1,
});
}
locationMenu.updateLocations(users);
}
} else {
print("Error loading data: " + req.status + " " + req.statusText + ", " + req.errorCode + ": " + req.responseText);
locationMenu.setError();
}
}
}
req.open("GET", scriptUrl);
req.send();

View file

@ -0,0 +1,20 @@
* {
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
font-size: 14px;
}
#promptTextEdit {
color: #425d72;
}
#promptTextEdit:!enabled {
color: #7f7f7f;
}
#promptGutterLabel {
color: #a9bbc3;
}
#promptGutterLabel:!enabled {
color: #7f7f7f;
}

View file

@ -102,18 +102,11 @@ const int STARTUP_JITTER_SAMPLES = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / 2
// Startup optimistically with small jitter buffer that
// will start playback on the second received audio packet.
const int MIRROR_VIEW_TOP_PADDING = 5;
const int MIRROR_VIEW_LEFT_PADDING = 10;
const int MIRROR_VIEW_WIDTH = 265;
const int MIRROR_VIEW_HEIGHT = 215;
const float MIRROR_FULLSCREEN_DISTANCE = 0.35f;
const float MIRROR_REARVIEW_DISTANCE = 0.65f;
const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f;
const float MIRROR_FIELD_OF_VIEW = 30.0f;
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js";
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (message.size() > 0) {
QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate);
@ -171,6 +164,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_bytesPerSecond(0),
_nodeBoundsDisplay(this),
_previousScriptLocation(),
_applicationOverlay(),
_runningScriptsWidget(new RunningScriptsWidget(_window)),
_runningScriptsWidgetWasVisible(false),
_trayIcon(new QSystemTrayIcon(_window))
@ -370,7 +364,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
qDebug() << "This is a first run...";
// clear the scripts, and set out script to our default scripts
clearScriptsBeforeRunning();
loadScript("http://public.highfidelity.io/scripts/defaultScripts.js");
loadScript(DEFAULT_SCRIPTS_JS_URL);
QMutexLocker locker(&_settingsMutex);
_settings->setValue("firstRun",QVariant(false));
@ -630,10 +624,12 @@ void Application::paintGL() {
if (OculusManager::isConnected()) {
OculusManager::display(whichCamera);
} else if (TV3DManager::isConnected()) {
_glowEffect.prepare();
TV3DManager::display(whichCamera);
_glowEffect.render();
} else {
_glowEffect.prepare();
@ -652,7 +648,7 @@ void Application::paintGL() {
_rearMirrorTools->render(true);
}
displayOverlay();
_applicationOverlay.renderOverlay();
}
_frameCount++;
@ -2594,183 +2590,6 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
}
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
void Application::displayOverlay() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()");
// Render 2D overlay: I/O level bar graphs and text
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// Display a single screen-size quad to create an alpha blended 'collision' flash
if (_audio.getCollisionFlashesScreen()) {
float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude();
const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f;
if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) {
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
}
}
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int AUDIO_METER_INSET = 2;
const int MUTE_ICON_PADDING = 10;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET;
const int AUDIO_METER_HEIGHT = 8;
const int AUDIO_METER_GAP = 5;
const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP;
int audioMeterY;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
} else {
audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
}
const float AUDIO_METER_BLUE[] = {0.0, 0.0, 1.0};
const float AUDIO_METER_GREEN[] = {0.0, 1.0, 0.0};
const float AUDIO_METER_RED[] = {1.0, 0.0, 0.0};
const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH;
const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH;
const float CLIPPING_INDICATOR_TIME = 1.0f;
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.f);
const float METER_LOUDNESS_SCALE = 2.8f / 5.f;
const float LOG2_LOUDNESS_FLOOR = 11.f;
float audioLevel = 0.f;
float loudness = _audio.getLastInputLoudness() + 1.f;
_trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness;
float log2loudness = log(_trailingAudioLoudness) / LOG2;
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
} else {
audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
}
if (audioLevel > AUDIO_METER_SCALE_WIDTH) {
audioLevel = AUDIO_METER_SCALE_WIDTH;
}
bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
if ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) {
const float MAX_MAGNITUDE = 0.7f;
float magnitude = MAX_MAGNITUDE * (1 - _audio.getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME);
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), magnitude, 1.0f);
}
_audio.renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP,
audioMeterY,
Menu::getInstance()->isOptionChecked(MenuOption::Mirror));
_audio.renderScope(_glWidget->width(), _glWidget->height());
glBegin(GL_QUADS);
if (isClipping) {
glColor3f(1, 0, 0);
} else {
glColor3f(0.475f, 0.475f, 0.475f);
}
audioMeterY += AUDIO_METER_HEIGHT;
glColor3f(0, 0, 0);
// Draw audio meter background Quad
glVertex2i(AUDIO_METER_X, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT);
glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT);
if (audioLevel > AUDIO_RED_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_RED);
} else {
glColor3f(1, 1, 1);
}
// Draw Red Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_RED_START;
}
if (audioLevel > AUDIO_GREEN_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_GREEN);
} else {
glColor3f(1, 1, 1);
}
// Draw Green Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_GREEN_START;
}
// Draw Blue Quad
if (!isClipping) {
glColor3fv(AUDIO_METER_BLUE);
} else {
glColor3f(1, 1, 1);
}
// Draw Blue (low level) quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glEnd();
if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
_myAvatar->renderHeadMouse(_glWidget->width(), _glWidget->height());
}
// Display stats and log text onscreen
glLineWidth(1.0f);
glPointSize(1.0f);
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount();
// Onscreen text about position, servers, etc
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess);
// Bandwidth meter
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) {
Stats::drawBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68);
_bandwidthMeter.render(_glWidget->width(), _glWidget->height());
}
}
// Show on-screen msec timer
if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
char frameTimer[10];
quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
int timerBottom =
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
? 80 : 20;
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT);
}
_nodeBoundsDisplay.drawOverlay();
// give external parties a change to hook in
emit renderingOverlay();
_overlays.render2D();
glPopMatrix();
}
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
float horizontalScale = _glWidget->width() / 2.0f;
float verticalScale = _glWidget->height() / 2.0f;
@ -3437,16 +3256,21 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
return _scriptEnginesHash[scriptName];
}
// start the script on a new thread...
QUrl scriptUrl(scriptName);
ScriptEngine* scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
_scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine);
ScriptEngine* scriptEngine;
if (scriptName.isNull()) {
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
} else {
// start the script on a new thread...
QUrl scriptUrl(scriptName);
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
_scriptEnginesHash.insert(scriptName, scriptEngine);
if (!scriptEngine->hasScript()) {
qDebug() << "Application::loadScript(), script failed to load...";
return NULL;
if (!scriptEngine->hasScript()) {
qDebug() << "Application::loadScript(), script failed to load...";
return NULL;
}
_runningScriptsWidget->setRunningScripts(getRunningScripts());
}
_runningScriptsWidget->setRunningScripts(getRunningScripts());
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
// we can use the same ones from the application.
@ -3545,6 +3369,12 @@ void Application::reloadAllScripts() {
stopAllScripts(true);
}
void Application::loadDefaultScripts() {
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
loadScript(DEFAULT_SCRIPTS_JS_URL);
}
}
void Application::manageRunningScriptsWidgetVisibility(bool shown) {
if (_runningScriptsWidgetWasVisible && shown) {
_runningScriptsWidget->show();

View file

@ -83,6 +83,7 @@
#include "ui/LogDialog.h"
#include "ui/UpdateDialog.h"
#include "ui/overlays/Overlays.h"
#include "ui/ApplicationOverlay.h"
#include "ui/RunningScriptsWidget.h"
#include "voxels/VoxelFade.h"
#include "voxels/VoxelHideShowThread.h"
@ -116,6 +117,15 @@ static const QString CUSTOM_URL_SCHEME = "hifi:";
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
static const float BILLBOARD_DISTANCE = 5.0f; // meters
static const int MIRROR_VIEW_TOP_PADDING = 5;
static const int MIRROR_VIEW_LEFT_PADDING = 10;
static const int MIRROR_VIEW_WIDTH = 265;
static const int MIRROR_VIEW_HEIGHT = 215;
static const float MIRROR_FULLSCREEN_DISTANCE = 0.35f;
static const float MIRROR_REARVIEW_DISTANCE = 0.65f;
static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f;
static const float MIRROR_FIELD_OF_VIEW = 30.0f;
class Application : public QApplication {
Q_OBJECT
@ -182,6 +192,7 @@ public:
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
VoxelSystem* getVoxels() { return &_voxels; }
VoxelTree* getVoxelTree() { return _voxels.getTree(); }
const VoxelPacketProcessor& getVoxelPacketProcessor() const { return _voxelProcessor; }
ParticleTreeRenderer* getParticles() { return &_particles; }
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
ModelTreeRenderer* getModels() { return &_models; }
@ -205,6 +216,13 @@ public:
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
QUndoStack* getUndoStack() { return &_undoStack; }
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
Overlays& getOverlays() { return _overlays; }
float getFps() const { return _fps; }
float getPacketsPerSecond() const { return _packetsPerSecond; }
float getBytesPerSecond() const { return _bytesPerSecond; }
const glm::vec3& getViewMatrixTranslation() const { return _viewMatrixTranslation; }
/// if you need to access the application settings, use lockSettings()/unlockSettings()
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
@ -305,11 +323,12 @@ public slots:
void loadScriptURLDialog();
void toggleLogDialog();
void initAvatarAndViewFrustum();
ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false);
ScriptEngine* loadScript(const QString& fileNameString = QString(), bool loadScriptFromEditor = false);
void scriptFinished(const QString& scriptName);
void stopAllScripts(bool restart = false);
void stopScript(const QString& scriptName);
void reloadAllScripts();
void loadDefaultScripts();
void toggleRunningScriptsWidget();
void uploadHead();
@ -377,7 +396,6 @@ private:
glm::vec3 getSunDirection();
void updateShadowMap();
void displayOverlay();
void renderRearViewMirror(const QRect& region, bool billboard = false);
void renderViewFrustum(ViewFrustum& viewFrustum);
@ -552,6 +570,7 @@ private:
TouchEvent _lastTouchEvent;
Overlays _overlays;
ApplicationOverlay _applicationOverlay;
AudioReflector _audioReflector;
RunningScriptsWidget* _runningScriptsWidget;

View file

@ -73,6 +73,11 @@ const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
const float MUTE_RADIUS = 50;
const QString CONSOLE_TITLE = "Scripting Console";
const float CONSOLE_WINDOW_OPACITY = 0.95f;
const int CONSOLE_WIDTH = 800;
const int CONSOLE_HEIGHT = 200;
Menu::Menu() :
_actionHash(),
_audioJitterBufferSamples(0),
@ -81,6 +86,7 @@ Menu::Menu() :
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
_jsConsole(NULL),
_octreeStatsDialog(NULL),
_lodToolsDialog(NULL),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
@ -227,6 +233,12 @@ Menu::Menu() :
_chatWindow = new ChatWindow(Application::getInstance()->getWindow());
#endif
addActionToQMenuAndActionHash(toolsMenu,
MenuOption::Console,
Qt::CTRL | Qt::ALT | Qt::Key_J,
this,
SLOT(toggleConsole()));
QMenu* viewMenu = addMenu("View");
addCheckableActionToQMenuAndActionHash(viewMenu,
@ -1258,6 +1270,25 @@ void Menu::toggleChat() {
#endif
}
void Menu::toggleConsole() {
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_jsConsole) {
QDialog* dialog = new QDialog(mainWindow, Qt::WindowStaysOnTopHint);
QVBoxLayout* layout = new QVBoxLayout(dialog);
dialog->setLayout(new QVBoxLayout(dialog));
dialog->resize(QSize(CONSOLE_WIDTH, CONSOLE_HEIGHT));
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(new JSConsole(dialog));
dialog->setWindowOpacity(CONSOLE_WINDOW_OPACITY);
dialog->setWindowTitle(CONSOLE_TITLE);
_jsConsole = dialog;
}
_jsConsole->setVisible(!_jsConsole->isVisible());
}
void Menu::audioMuteToggled() {
QAction *muteAction = _actionHash.value(MenuOption::MuteAudio);
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());

View file

@ -26,6 +26,7 @@
#include "location/LocationManager.h"
#include "ui/PreferencesDialog.h"
#include "ui/ChatWindow.h"
#include "ui/JSConsole.h"
#include "ui/ScriptEditorWindow.h"
const float ADJUST_LOD_DOWN_FPS = 40.0;
@ -189,6 +190,7 @@ private slots:
void showMetavoxelEditor();
void showScriptEditor();
void showChat();
void toggleConsole();
void toggleChat();
void audioMuteToggled();
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
@ -243,6 +245,7 @@ private:
QPointer<MetavoxelEditor> _MetavoxelEditor;
QPointer<ScriptEditorWindow> _ScriptEditor;
QPointer<ChatWindow> _chatWindow;
QDialog* _jsConsole;
OctreeStatsDialog* _octreeStatsDialog;
LodToolsDialog* _lodToolsDialog;
int _maxVoxels;
@ -308,6 +311,7 @@ namespace MenuOption {
const QString CollideWithParticles = "Collide With Particles";
const QString CollideWithVoxels = "Collide With Voxels";
const QString Collisions = "Collisions";
const QString Console = "Console...";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DecreaseVoxelSize = "Decrease Voxel Size";
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";

View file

@ -49,9 +49,9 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state._translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
state._rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
* joint.rotation;
@ -59,17 +59,34 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
// likewise with the eye joints
glm::mat4 inverse = glm::inverse(parentState.transform * glm::translate(state.translation) *
glm::mat4 inverse = glm::inverse(parentState._transform * glm::translate(state._translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientation() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
_owningHead->getSaccade() - _translation, 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
state.rotation = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
state._rotation = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation;
}
void FaceModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex != -1) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, state);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(parentState, joint, state);
}
}
Model::updateJointState(index);
}
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
if (!isActive()) {
return false;

View file

@ -28,7 +28,8 @@ public:
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void updateJointState(int index);
/// Retrieve the positions of up to two eye meshes.
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;

View file

@ -45,7 +45,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
}
int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex);
if (jointIndex != -1) {
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true, PALM_PRIORITY);
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), PALM_PRIORITY);
}
}
return;
@ -188,8 +188,8 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
setJointRotation(parentJointIndex, palmRotation, true, PALM_PRIORITY);
_jointStates[jointIndex].rotation = glm::quat();
setJointRotation(parentJointIndex, palmRotation, PALM_PRIORITY);
_jointStates[jointIndex]._rotation = glm::quat();
} else {
setJointPosition(jointIndex, palm.getPosition(), palmRotation,
@ -199,10 +199,10 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
void SkeletonModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(index);
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex != -1) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (index == geometry.leanJointIndex) {
maybeUpdateLeanRotation(parentState, joint, state);
@ -217,9 +217,9 @@ void SkeletonModel::updateJointState(int index) {
Model::updateJointState(index);
if (index == _geometry->getFBXGeometry().rootJointIndex) {
state.transform[3][0] = 0.0f;
state.transform[3][1] = 0.0f;
state.transform[3][2] = 0.0f;
state._transform[3][0] = 0.0f;
state._transform[3][1] = 0.0f;
state._transform[3][2] = 0.0f;
}
}
@ -229,9 +229,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
}
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state._translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
state._rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
glm::normalize(inverse * axes[0])) * joint.rotation;
}
@ -255,11 +255,11 @@ void SkeletonModel::renderJointConstraints(int jointIndex) {
do {
const FBXJoint& joint = geometry.joints.at(jointIndex);
const JointState& jointState = _jointStates.at(jointIndex);
glm::vec3 position = extractTranslation(jointState.transform) + _translation;
glm::vec3 position = extractTranslation(jointState._transform) + _translation;
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _jointStates.at(joint.parentIndex).combinedRotation;
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _jointStates.at(joint.parentIndex)._combinedRotation;
glm::vec3 rotationAxis = glm::axis(parentRotation);
glRotatef(glm::degrees(glm::angle(parentRotation)), rotationAxis.x, rotationAxis.y, rotationAxis.z);
float fanScale = directionSize * 0.75f;
@ -292,7 +292,7 @@ void SkeletonModel::renderJointConstraints(int jointIndex) {
}
glPopMatrix();
renderOrientationDirections(position, jointState.combinedRotation, directionSize);
renderOrientationDirections(position, jointState._combinedRotation, directionSize);
jointIndex = joint.parentIndex;
} while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree);
@ -355,12 +355,12 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
setJointRotation(shoulderJointIndex, shoulderRotation, true, PALM_PRIORITY);
setJointRotation(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector,
wristPosition - elbowPosition) * shoulderRotation, true, PALM_PRIORITY);
wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
setJointRotation(jointIndex, rotation, true, PALM_PRIORITY);
setJointRotation(jointIndex, rotation, PALM_PRIORITY);
}
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {

View file

@ -50,7 +50,8 @@ void OculusManager::connect() {
_sensorDevice = *_hmdDevice->GetSensor();
_sensorFusion = new SensorFusion;
_sensorFusion->AttachToSensor(_sensorDevice);
_sensorFusion->SetPredictionEnabled(true);
HMDInfo info;
_hmdDevice->GetDeviceInfo(&info);
_stereoConfig.SetHMDInfo(info);
@ -81,7 +82,13 @@ void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenH
void OculusManager::display(Camera& whichCamera) {
#ifdef HAVE_LIBOVR
Application::getInstance()->getGlowEffect()->prepare();
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
// We only need to render the overlays to a texture once, then we just render the texture as a quad
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
const bool displayOverlays = false;
Application::getInstance()->getGlowEffect()->prepare();
// render the left eye view to the left side of the screen
const StereoEyeParams& leftEyeParams = _stereoConfig.GetEyeRenderParams(StereoEye_Left);
@ -100,6 +107,10 @@ void OculusManager::display(Camera& whichCamera) {
Application::getInstance()->displaySide(whichCamera);
if (displayOverlays) {
applicationOverlay.displayOverlayTextureOculus(whichCamera);
}
// and the right eye to the right side
const StereoEyeParams& rightEyeParams = _stereoConfig.GetEyeRenderParams(StereoEye_Right);
glMatrixMode(GL_PROJECTION);
@ -115,6 +126,10 @@ void OculusManager::display(Camera& whichCamera) {
Application::getInstance()->displaySide(whichCamera);
if (displayOverlays) {
applicationOverlay.displayOverlayTextureOculus(whichCamera);
}
glPopMatrix();
// restore our normal viewport
@ -187,7 +202,7 @@ void OculusManager::reset() {
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
#ifdef HAVE_LIBOVR
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
_sensorFusion->GetPredictedOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
#endif
}

View file

@ -79,7 +79,7 @@ static void setPalm(float deltaTime, int index) {
glm::vec3 position;
glm::quat rotation;
Model* skeletonModel = &Application::getInstance()->getAvatar()->getSkeletonModel();
SkeletonModel* skeletonModel = &Application::getInstance()->getAvatar()->getSkeletonModel();
int jointIndex;
glm::quat inverseRotation = glm::inverse(Application::getInstance()->getAvatar()->getOrientation());
if (index == LEFT_HAND_INDEX) {

View file

@ -139,8 +139,7 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
if (_isEmpty && _renderMode != DIFFUSE_ADD_MODE) {
// copy the primary to the screen
if (QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) {
QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO);
QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO);
} else {
maybeBind(destFBO);
glEnable(GL_TEXTURE_2D);

View file

@ -135,13 +135,11 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
program.release();
}
QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry) {
QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) {
QVector<JointState> jointStates;
foreach (const FBXJoint& joint, geometry.joints) {
JointState state;
state.translation = joint.translation;
state.rotation = joint.rotation;
state.animationPriority = 0.0f;
state.setFBXJoint(joint);
jointStates.append(state);
}
@ -160,23 +158,17 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
continue;
}
JointState& state = jointStates[i];
const FBXJoint& joint = geometry.joints[i];
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
_rootIndex = i;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
state.combinedRotation = _rotation * combinedRotation;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
state.updateWorldTransform(baseTransform, _rotation);
++numJointsSet;
jointIsSet[i] = true;
} else if (jointIsSet[parentIndex]) {
const JointState& parentState = jointStates.at(parentIndex);
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
state.transform = parentState.transform * glm::translate(state.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
state.combinedRotation = parentState.combinedRotation * combinedRotation;
state.updateWorldTransform(parentState._transform, parentState._combinedRotation);
++numJointsSet;
jointIsSet[i] = true;
}
@ -372,7 +364,7 @@ void Model::reset() {
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].rotation = geometry.joints.at(i).rotation;
_jointStates[i]._rotation = geometry.joints.at(i).rotation;
}
}
@ -582,7 +574,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
rotation = _jointStates.at(index).rotation;
rotation = _jointStates.at(index)._rotation;
const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation;
return glm::abs(rotation.x - defaultRotation.x) >= EPSILON ||
glm::abs(rotation.y - defaultRotation.y) >= EPSILON ||
@ -593,13 +585,13 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
if (priority >= state.animationPriority) {
if (priority >= state._animationPriority) {
if (valid) {
state.rotation = rotation;
state.animationPriority = priority;
} else if (priority == state.animationPriority) {
state.rotation = _geometry->getFBXGeometry().joints.at(index).rotation;
state.animationPriority = 0.0f;
state._rotation = rotation;
state._animationPriority = priority;
} else if (priority == state._animationPriority) {
state._rotation = _geometry->getFBXGeometry().joints.at(index).rotation;
state._animationPriority = 0.0f;
}
}
}
@ -632,7 +624,7 @@ bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
position = _translation + extractTranslation(_jointStates[jointIndex].transform);
position = _translation + extractTranslation(_jointStates[jointIndex]._transform);
return true;
}
@ -640,7 +632,7 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind)
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
rotation = _jointStates[jointIndex].combinedRotation *
rotation = _jointStates[jointIndex]._combinedRotation *
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
return true;
@ -858,11 +850,11 @@ void Model::updateShapePositions() {
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
// shape position and rotation need to be in world-frame
glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition);
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i]._combinedRotation * joint.shapePosition);
glm::vec3 worldPosition = extractTranslation(_jointStates[i]._transform) + jointToShapeOffset + _translation;
Shape* shape = _jointShapes[i];
shape->setPosition(worldPosition);
shape->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
shape->setRotation(_jointStates[i]._combinedRotation * joint.shapeRotation);
float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius();
if (distance > _boundingRadius) {
_boundingRadius = distance;
@ -884,12 +876,12 @@ bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
float radiusScale = extractUniformScale(_scale);
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 end = extractTranslation(_jointStates[i].transform);
glm::vec3 end = extractTranslation(_jointStates[i]._transform);
float endRadius = joint.boneRadius * radiusScale;
glm::vec3 start = end;
float startRadius = joint.boneRadius * radiusScale;
if (joint.parentIndex != -1) {
start = extractTranslation(_jointStates[joint.parentIndex].transform);
start = extractTranslation(_jointStates[joint.parentIndex]._transform);
startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale;
}
// for now, use average of start and end radii
@ -1115,7 +1107,7 @@ void Model::simulateInternal(float deltaTime) {
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix;
state.clusterMatrices[j] = _jointStates[cluster.jointIndex]._transform * cluster.inverseBindMatrix;
}
}
@ -1127,21 +1119,15 @@ void Model::simulateInternal(float deltaTime) {
void Model::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(index);
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex == -1) {
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
state.combinedRotation = _rotation * combinedRotation;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
state.updateWorldTransform(baseTransform, _rotation);
} else {
const JointState& parentState = _jointStates.at(joint.parentIndex);
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
state.transform = parentState.transform * glm::translate(state.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
state.combinedRotation = parentState.combinedRotation * combinedRotation;
state.updateWorldTransform(parentState._transform, parentState._combinedRotation);
}
}
@ -1169,22 +1155,22 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
glm::quat endRotation;
if (useRotation) {
getJointRotation(jointIndex, endRotation, true);
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), priority);
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), true, priority);
getJointRotation(jointIndex, endRotation, true);
}
// then, we go from the joint upwards, rotating the end as close as possible to the target
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].transform);
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex]._transform);
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
int index = freeLineage.at(j);
const FBXJoint& joint = geometry.joints.at(index);
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (!(joint.isFree || allIntermediatesFree)) {
continue;
}
JointState& state = _jointStates[index];
glm::vec3 jointPosition = extractTranslation(state.transform);
glm::vec3 jointPosition = extractTranslation(state._transform);
glm::vec3 jointVector = endPosition - jointPosition;
glm::quat oldCombinedRotation = state.combinedRotation;
glm::quat oldCombinedRotation = state._combinedRotation;
glm::quat combinedDelta;
float combinedWeight;
if (useRotation) {
@ -1202,7 +1188,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
for (int k = j - 1; k > 0; k--) {
int index = freeLineage.at(k);
updateJointState(index);
positionSum += extractTranslation(_jointStates.at(index).transform);
positionSum += extractTranslation(_jointStates.at(index)._transform);
}
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector));
@ -1213,8 +1199,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
1.0f / (combinedWeight + 1.0f));
}
}
applyRotationDelta(index, combinedDelta, priority);
glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation);
applyRotationDelta(index, combinedDelta, true, priority);
glm::quat actualDelta = state._combinedRotation * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) {
endRotation = actualDelta * endRotation;
@ -1231,35 +1217,34 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
return true;
}
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind, float priority) {
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
JointState& state = _jointStates[jointIndex];
if (priority >= state.animationPriority) {
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation *
glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation :
_geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation);
state.animationPriority = priority;
if (priority >= state._animationPriority) {
state._rotation = state._rotation * glm::inverse(state._combinedRotation) * rotation *
glm::inverse(_geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation);
state._animationPriority = priority;
}
return true;
}
void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(jointIndex);
JointState& state = _jointStates[jointIndex];
const FBXJoint& joint = state.getFBXJoint();
glm::mat4 parentTransform;
if (joint.parentIndex == -1) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
parentTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
} else {
parentTransform = _jointStates.at(joint.parentIndex).transform;
parentTransform = _jointStates.at(joint.parentIndex)._transform;
}
JointState& state = _jointStates[jointIndex];
glm::vec3 preTranslation = extractTranslation(joint.preTransform * glm::mat4_cast(joint.preRotation *
state.rotation * joint.postRotation) * joint.postTransform);
state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation;
state._rotation * joint.postRotation) * joint.postTransform);
state._translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation;
}
bool Model::restoreJointPosition(int jointIndex, float percent, float priority) {
@ -1271,11 +1256,11 @@ bool Model::restoreJointPosition(int jointIndex, float percent, float priority)
foreach (int index, freeLineage) {
JointState& state = _jointStates[index];
if (priority == state.animationPriority) {
if (priority == state._animationPriority) {
const FBXJoint& joint = geometry.joints.at(index);
state.rotation = safeMix(state.rotation, joint.rotation, percent);
state.translation = glm::mix(state.translation, joint.translation, percent);
state.animationPriority = 0.0f;
state._rotation = safeMix(state._rotation, joint.rotation, percent);
state._translation = glm::mix(state._translation, joint.translation, percent);
state._animationPriority = 0.0f;
}
}
return true;
@ -1297,23 +1282,23 @@ float Model::getLimbLength(int jointIndex) const {
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
JointState& state = _jointStates[jointIndex];
if (priority < state.animationPriority) {
if (priority < state._animationPriority) {
return;
}
state.animationPriority = priority;
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
state._animationPriority = priority;
const FBXJoint& joint = state.getFBXJoint();
if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) &&
joint.rotationMax == glm::vec3(PI, PI, PI))) {
// no constraints
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * delta * state.combinedRotation;
state.combinedRotation = delta * state.combinedRotation;
state._rotation = state._rotation * glm::inverse(state._combinedRotation) * delta * state._combinedRotation;
state._combinedRotation = delta * state._combinedRotation;
return;
}
glm::quat targetRotation = delta * state.combinedRotation;
glm::vec3 eulers = safeEulerAngles(state.rotation * glm::inverse(state.combinedRotation) * targetRotation);
glm::quat targetRotation = delta * state._combinedRotation;
glm::vec3 eulers = safeEulerAngles(state._rotation * glm::inverse(state._combinedRotation) * targetRotation);
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
state.rotation = newRotation;
state._combinedRotation = state._combinedRotation * glm::inverse(state._rotation) * newRotation;
state._rotation = newRotation;
}
const int BALL_SUBDIVISIONS = 10;
@ -1851,10 +1836,10 @@ void AnimationHandle::simulate(float deltaTime) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping];
if (_priority >= state.animationPriority) {
state.rotation = frame.rotations.at(i);
state.animationPriority = _priority;
JointState& state = _model->_jointStates[mapping];
if (_priority >= state._animationPriority) {
state._rotation = frame.rotations.at(i);
state._animationPriority = _priority;
}
}
}
@ -1875,10 +1860,10 @@ void AnimationHandle::simulate(float deltaTime) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping];
if (_priority >= state.animationPriority) {
state.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
state.animationPriority = _priority;
JointState& state = _model->_jointStates[mapping];
if (_priority >= state._animationPriority) {
state._rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
state._animationPriority = _priority;
}
}
}
@ -1888,10 +1873,32 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping];
if (_priority == state.animationPriority) {
state.animationPriority = newPriority;
JointState& state = _model->_jointStates[mapping];
if (_priority == state._animationPriority) {
state._animationPriority = newPriority;
}
}
}
}
// ----------------------------------------------------------------------------
// JointState TODO: move this class to its own files
// ----------------------------------------------------------------------------
JointState::JointState() :
_translation(0.0f),
_animationPriority(0.0f),
_fbxJoint(NULL) {
}
void JointState::setFBXJoint(const FBXJoint& joint) {
assert(&joint != NULL);
_translation = joint.translation;
_rotation = joint.rotation;
_fbxJoint = &joint;
}
void JointState::updateWorldTransform(const glm::mat4& baseTransform, const glm::quat& parentRotation) {
glm::quat combinedRotation = _fbxJoint->preRotation * _rotation * _fbxJoint->postRotation;
_transform = baseTransform * glm::translate(_translation) * _fbxJoint->preTransform * glm::mat4_cast(combinedRotation) * _fbxJoint->postTransform;
_combinedRotation = parentRotation * combinedRotation;
}

View file

@ -30,6 +30,25 @@ class Shape;
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
class JointState {
public:
JointState();
void setFBXJoint(const FBXJoint& joint);
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
void updateWorldTransform(const glm::mat4& baseTransform, const glm::quat& parentRotation);
glm::vec3 _translation; // translation relative to parent
glm::quat _rotation; // rotation relative to parent
glm::mat4 _transform; // rotation to world frame + translation in model frame
glm::quat _combinedRotation; // rotation from joint local to world frame
float _animationPriority; // the priority of the animation affecting this joint
private:
const FBXJoint* _fbxJoint; // JointState does not own its FBXJoint
};
/// A generic 3D model displaying geometry loaded from a URL.
class Model : public QObject {
@ -182,15 +201,6 @@ protected:
bool _snappedToCenter; /// are we currently snapped to center
int _rootIndex;
class JointState {
public:
glm::vec3 translation; // translation relative to parent
glm::quat rotation; // rotation relative to parent
glm::mat4 transform; // rotation to world frame + translation in model frame
glm::quat combinedRotation; // rotation from joint local to world frame
float animationPriority; // the priority of the animation affecting this joint
};
bool _shapesAreDirty;
QVector<JointState> _jointStates;
QVector<Shape*> _jointShapes;
@ -221,7 +231,7 @@ protected:
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false, float priority = 1.0f);
bool setJointRotation(int jointIndex, const glm::quat& rotation, float priority = 1.0f);
void setJointTranslation(int jointIndex, const glm::vec3& translation);

View file

@ -0,0 +1,343 @@
//
// ApplicationOverlay.cpp
// interface/src/ui/overlays
//
// Created by Benjamin Arnold on 5/27/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 "InterfaceConfig.h"
#include <QOpenGLFramebufferObject>
#include <PerfStat.h>
#include "Application.h"
#include "ApplicationOverlay.h"
#include "ui/Stats.h"
ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL) {
}
ApplicationOverlay::~ApplicationOverlay() {
if (_framebufferObject != NULL) {
delete _framebufferObject;
}
}
const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
// 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()");
Application* application = Application::getInstance();
Overlays& overlays = application->getOverlays();
QGLWidget* glWidget = application->getGLWidget();
MyAvatar* myAvatar = application->getAvatar();
Audio* audio = application->getAudio();
const VoxelPacketProcessor& voxelPacketProcessor = application->getVoxelPacketProcessor();
BandwidthMeter* bandwidthMeter = application->getBandwidthMeter();
NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay();
if (renderToTexture) {
getFramebufferObject()->bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Render 2D overlay: I/O level bar graphs and text
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, glWidget->width(), glWidget->height(), 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// Display a single screen-size quad to create an alpha blended 'collision' flash
if (audio->getCollisionFlashesScreen()) {
float collisionSoundMagnitude = audio->getCollisionSoundMagnitude();
const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f;
if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) {
renderCollisionOverlay(glWidget->width(), glWidget->height(), audio->getCollisionSoundMagnitude());
}
}
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int AUDIO_METER_INSET = 2;
const int MUTE_ICON_PADDING = 10;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET;
const int AUDIO_METER_HEIGHT = 8;
const int AUDIO_METER_GAP = 5;
const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP;
int audioMeterY;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
} else {
audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
}
const float AUDIO_METER_BLUE[] = { 0.0, 0.0, 1.0 };
const float AUDIO_METER_GREEN[] = { 0.0, 1.0, 0.0 };
const float AUDIO_METER_RED[] = { 1.0, 0.0, 0.0 };
const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH;
const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH;
const float CLIPPING_INDICATOR_TIME = 1.0f;
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.f);
const float METER_LOUDNESS_SCALE = 2.8f / 5.f;
const float LOG2_LOUDNESS_FLOOR = 11.f;
float audioLevel = 0.f;
float loudness = audio->getLastInputLoudness() + 1.f;
_trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness;
float log2loudness = log(_trailingAudioLoudness) / LOG2;
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
} else {
audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH;
}
if (audioLevel > AUDIO_METER_SCALE_WIDTH) {
audioLevel = AUDIO_METER_SCALE_WIDTH;
}
bool isClipping = ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
if ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) {
const float MAX_MAGNITUDE = 0.7f;
float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME);
renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f);
}
audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP,
audioMeterY,
Menu::getInstance()->isOptionChecked(MenuOption::Mirror));
audio->renderScope(glWidget->width(), glWidget->height());
glBegin(GL_QUADS);
if (isClipping) {
glColor3f(1, 0, 0);
} else {
glColor3f(0.475f, 0.475f, 0.475f);
}
audioMeterY += AUDIO_METER_HEIGHT;
glColor3f(0, 0, 0);
// Draw audio meter background Quad
glVertex2i(AUDIO_METER_X, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY);
glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT);
glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT);
if (audioLevel > AUDIO_RED_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_RED);
} else {
glColor3f(1, 1, 1);
}
// Draw Red Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_RED_START;
}
if (audioLevel > AUDIO_GREEN_START) {
if (!isClipping) {
glColor3fv(AUDIO_METER_GREEN);
} else {
glColor3f(1, 1, 1);
}
// Draw Green Quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
audioLevel = AUDIO_GREEN_START;
}
// Draw Blue Quad
if (!isClipping) {
glColor3fv(AUDIO_METER_BLUE);
} else {
glColor3f(1, 1, 1);
}
// Draw Blue (low level) quad
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET);
glEnd();
if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
myAvatar->renderHeadMouse(glWidget->width(), glWidget->height());
}
// Display stats and log text onscreen
glLineWidth(1.0f);
glPointSize(1.0f);
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
int voxelPacketsToProcess = voxelPacketProcessor.packetsToProcessCount();
// Onscreen text about position, servers, etc
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess);
// Bandwidth meter
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) {
Stats::drawBackground(0x33333399, glWidget->width() - 296, glWidget->height() - 68, 296, 68);
bandwidthMeter->render(glWidget->width(), glWidget->height());
}
}
// Show on-screen msec timer
if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
char frameTimer[10];
quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
int timerBottom =
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
? 80 : 20;
drawText(glWidget->width() - 100, glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT);
}
nodeBoundsDisplay.drawOverlay();
// give external parties a change to hook in
emit application->renderingOverlay();
overlays.render2D();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
if (renderToTexture) {
getFramebufferObject()->release();
}
}
// Draws the FBO texture for the screen
void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) {
Application* application = Application::getInstance();
QGLWidget* glWidget = application->getGLWidget();
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, glWidget->width(), glWidget->height(), 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glBegin(GL_QUADS);
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();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane.
void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
Application* application = Application::getInstance();
QGLWidget* glWidget = application->getGLWidget();
MyAvatar* myAvatar = application->getAvatar();
const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation();
// Calculates the world space width and height of the texture based on a desired FOV
const float overlayFov = whichCamera.getFieldOfView() * PI / 180.0f;
const float overlayDistance = 1;
const float overlayAspectRatio = glWidget->width() / (float)glWidget->height();
const float overlayHeight = overlayDistance * tan(overlayFov);
const float overlayWidth = overlayHeight * overlayAspectRatio;
const float halfOverlayWidth = overlayWidth / 2;
const float halfOverlayHeight = overlayHeight / 2;
glActiveTexture(GL_TEXTURE0);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Transform to world space
glm::quat rotation = whichCamera.getRotation();
glm::vec3 axis2 = glm::axis(rotation);
glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
// Translate to the front of the camera
glm::vec3 pos = whichCamera.getPosition();
glm::quat rot = myAvatar->getOrientation();
glm::vec3 axis = glm::axis(rot);
pos += rot * glm::vec3(0.0, 0.0, -overlayDistance);
glTranslatef(pos.x, pos.y, pos.z);
glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
glBegin(GL_QUADS);
glTexCoord2f(1, 0); glVertex3f(-halfOverlayWidth, halfOverlayHeight, 0);
glTexCoord2f(0, 0); glVertex3f(halfOverlayWidth, halfOverlayHeight, 0);
glTexCoord2f(0, 1); glVertex3f(halfOverlayWidth, -halfOverlayHeight, 0);
glTexCoord2f(1, 1); glVertex3f(-halfOverlayWidth, -halfOverlayHeight, 0);
glEnd();
glPopMatrix();
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_LIGHTING);
}
QOpenGLFramebufferObject* ApplicationOverlay::getFramebufferObject() {
if (!_framebufferObject) {
_framebufferObject = new QOpenGLFramebufferObject(Application::getInstance()->getGLWidget()->size());
glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
return _framebufferObject;
}

View file

@ -0,0 +1,38 @@
//
// ApplicationOverlay.h
// interface/src/ui/overlays
//
// Created by Benjamin Arnold on 5/27/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_ApplicationOverlay_h
#define hifi_ApplicationOverlay_h
class Overlays;
class QOpenGLFramebufferObject;
// Handles the drawing of the overlays to the scree
class ApplicationOverlay {
public:
ApplicationOverlay();
~ApplicationOverlay();
void renderOverlay(bool renderToTexture = false);
void displayOverlayTexture(Camera& whichCamera);
void displayOverlayTextureOculus(Camera& whichCamera);
// Getters
QOpenGLFramebufferObject* getFramebufferObject();
private:
QOpenGLFramebufferObject* _framebufferObject;
float _trailingAudioLoudness;
};
#endif // hifi_ApplicationOverlay_h

View file

@ -0,0 +1,229 @@
//
// JSConsole.cpp
// interface/src/ui
//
// Created by Ryan Huffman on 05/12/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 <QKeyEvent>
#include <QLabel>
#include <QScrollBar>
#include "Application.h"
#include "ScriptHighlighting.h"
#include "JSConsole.h"
const int NO_CURRENT_HISTORY_COMMAND = -1;
const int MAX_HISTORY_SIZE = 64;
const QString COMMAND_STYLE = "color: #266a9b;";
const QString RESULT_SUCCESS_STYLE = "color: #677373;";
const QString RESULT_ERROR_STYLE = "color: #d13b22;";
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\">&lt;</span>";
const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
QWidget(parent),
_ui(new Ui::Console),
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
_commandHistory(),
_scriptEngine(scriptEngine) {
_ui->setupUi(this);
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
_ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap);
_ui->promptTextEdit->installEventFilter(this);
QFile styleSheet(Application::resourcesPath() + "styles/console.qss");
if (styleSheet.open(QIODevice::ReadOnly)) {
QDir::setCurrent(Application::resourcesPath());
setStyleSheet(styleSheet.readAll());
}
connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom()));
connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput()));
if (_scriptEngine == NULL) {
_scriptEngine = Application::getInstance()->loadScript();
}
connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)),
this, SLOT(handleEvalutationFinished(QScriptValue, bool)));
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
resizeTextInput();
}
JSConsole::~JSConsole() {
delete _ui;
}
void JSConsole::executeCommand(const QString& command) {
_commandHistory.prepend(command);
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
_commandHistory.removeLast();
}
_ui->promptTextEdit->setDisabled(true);
appendMessage(">", "<span style='" + COMMAND_STYLE + "'>" + command.toHtmlEscaped() + "</span>");
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command));
resetCurrentCommandHistory();
}
void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) {
_ui->promptTextEdit->setDisabled(false);
// Make sure focus is still on this window - some commands are blocking and can take awhile to execute.
if (window()->isActiveWindow()) {
_ui->promptTextEdit->setFocus();
}
QString gutter = (isException || result.isError()) ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND;
QString resultColor = (isException || result.isError()) ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE;
QString resultStr = "<span style='" + resultColor + "'>" + result.toString().toHtmlEscaped() + "</span>";
appendMessage(gutter, resultStr);
}
void JSConsole::handlePrint(const QString& message) {
appendMessage("", message);
}
void JSConsole::mouseReleaseEvent(QMouseEvent* event) {
_ui->promptTextEdit->setFocus();
}
void JSConsole::showEvent(QShowEvent* event) {
_ui->promptTextEdit->setFocus();
}
bool JSConsole::eventFilter(QObject* sender, QEvent* event) {
if (sender == _ui->promptTextEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
int key = keyEvent->key();
if ((key == Qt::Key_Return || key == Qt::Key_Enter)) {
if (keyEvent->modifiers() & Qt::ShiftModifier) {
// If the shift key is being used then treat it as a regular return/enter. If this isn't done,
// a new QTextBlock isn't created.
keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier);
} else {
QString command = _ui->promptTextEdit->toPlainText().trimmed();
if (!command.isEmpty()) {
QTextCursor cursor = _ui->promptTextEdit->textCursor();
_ui->promptTextEdit->clear();
executeCommand(command);
}
return true;
}
} else if (key == Qt::Key_Down) {
// Go to the next command in history if the cursor is at the last line of the current command.
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
int blockCount = _ui->promptTextEdit->document()->blockCount();
if (blockNumber == blockCount - 1) {
setToNextCommandInHistory();
return true;
}
} else if (key == Qt::Key_Up) {
// Go to the previous command in history if the cursor is at the first line of the current command.
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
if (blockNumber == 0) {
setToPreviousCommandInHistory();
return true;
}
}
}
}
return false;
}
void JSConsole::setToNextCommandInHistory() {
if (_currentCommandInHistory >= 0) {
_currentCommandInHistory--;
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
setAndSelectCommand(_rootCommand);
} else {
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
}
}
}
void JSConsole::setToPreviousCommandInHistory() {
if (_currentCommandInHistory < (_commandHistory.length() - 1)) {
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
_rootCommand = _ui->promptTextEdit->toPlainText();
}
_currentCommandInHistory++;
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
}
}
void JSConsole::resetCurrentCommandHistory() {
_currentCommandInHistory = NO_CURRENT_HISTORY_COMMAND;
}
void JSConsole::resizeTextInput() {
_ui->promptTextEdit->setFixedHeight(_ui->promptTextEdit->document()->size().height());
_ui->promptTextEdit->updateGeometry();
}
void JSConsole::setAndSelectCommand(const QString& text) {
QTextCursor cursor = _ui->promptTextEdit->textCursor();
cursor.select(QTextCursor::Document);
cursor.deleteChar();
cursor.insertText(text);
cursor.movePosition(QTextCursor::End);
}
void JSConsole::scrollToBottom() {
QScrollBar* scrollBar = _ui->scrollArea->verticalScrollBar();
scrollBar->setValue(scrollBar->maximum());
}
void JSConsole::appendMessage(const QString& gutter, const QString& message) {
QWidget* logLine = new QWidget(_ui->logArea);
QHBoxLayout* layout = new QHBoxLayout(logLine);
layout->setMargin(0);
layout->setSpacing(4);
QLabel* gutterLabel = new QLabel(logLine);
QLabel* messageLabel = new QLabel(logLine);
gutterLabel->setFixedWidth(16);
gutterLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
gutterLabel->setStyleSheet("font-size: 14px; font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;");
messageLabel->setStyleSheet("font-size: 14px; font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;");
gutterLabel->setText(gutter);
messageLabel->setText(message);
layout->addWidget(gutterLabel);
layout->addWidget(messageLabel);
logLine->setLayout(layout);
layout->setAlignment(gutterLabel, Qt::AlignTop);
layout->setStretch(0, 0);
layout->setStretch(1, 1);
_ui->logArea->layout()->addWidget(logLine);
_ui->logArea->updateGeometry();
scrollToBottom();
}

View file

@ -0,0 +1,62 @@
//
// JSConsole.h
// interface/src/ui
//
// Created by Ryan Huffman on 05/12/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_JSConsole_h
#define hifi_JSConsole_h
#include <QDialog>
#include <QEvent>
#include <QObject>
#include <QWidget>
#include "ui_console.h"
#include "ScriptEngine.h"
class JSConsole : public QWidget {
Q_OBJECT
public:
JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL);
~JSConsole();
public slots:
void executeCommand(const QString& command);
signals:
void commandExecuting(const QString& command);
void commandFinished(const QString& result);
protected:
void setAndSelectCommand(const QString& command);
virtual bool eventFilter(QObject* sender, QEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void showEvent(QShowEvent* event);
protected slots:
void scrollToBottom();
void resizeTextInput();
void handleEvalutationFinished(QScriptValue result, bool isException);
void handlePrint(const QString& message);
private:
void appendMessage(const QString& gutter, const QString& message);
void setToNextCommandInHistory();
void setToPreviousCommandInHistory();
void resetCurrentCommandHistory();
Ui::Console* _ui;
int _currentCommandInHistory;
QList<QString> _commandHistory;
QString _rootCommand;
ScriptEngine* _scriptEngine;
};
#endif // hifi_JSConsole_h

View file

@ -29,6 +29,8 @@ PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : F
connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &PreferencesDialog::openHeadModelBrowser);
connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &PreferencesDialog::openBodyModelBrowser);
connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser);
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked,
Application::getInstance(), &Application::loadDefaultScripts);
}
void PreferencesDialog::accept() {

View file

@ -27,6 +27,9 @@
#include "Application.h"
#include "FlowLayout.h"
#include "JSConsole.h"
const int CONSOLE_HEIGHT = 150;
ScriptEditorWindow::ScriptEditorWindow() :
_ScriptEditorWindowUI(new Ui::ScriptEditorWindow),
@ -48,6 +51,10 @@ ScriptEditorWindow::ScriptEditorWindow() :
connect(new QShortcut(QKeySequence("Ctrl+S"), this), &QShortcut::activated, this,&ScriptEditorWindow::saveScriptClicked);
connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked);
connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked);
QWidget* console = new JSConsole(this);
console->setFixedHeight(CONSOLE_HEIGHT);
this->layout()->addWidget(console);
}
ScriptEditorWindow::~ScriptEditorWindow() {

258
interface/ui/console.ui Normal file
View file

@ -0,0 +1,258 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Console</class>
<widget class="QWidget" name="Console">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1055</width>
<height>205</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="styleSheet">
<string notr="true">QDialog { background: white }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1040</width>
<height>205</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: white;</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="logArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: white;</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="inputArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QLabel" name="promptGutterLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>16</width>
<height>23</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16</width>
<height>23</height>
</size>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">padding: 0px 0 0 0;</string>
</property>
<property name="text">
<string>&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="promptTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Inconsolata,Lucida Console,Andale Mono,Monaco</family>
<pointsize>-1</pointsize>
</font>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -134,8 +134,8 @@ color: #0e7077</string>
<rect>
<x>0</x>
<y>30</y>
<width>615</width>
<height>491</height>
<width>494</width>
<height>384</height>
</rect>
</property>
<property name="frameShape">
@ -154,9 +154,9 @@ color: #0e7077</string>
<property name="geometry">
<rect>
<x>0</x>
<y>-271</y>
<width>598</width>
<height>1018</height>
<y>-204</y>
<width>494</width>
<height>1091</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -612,6 +612,78 @@ color: #0e7077</string>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="avatarTitleLabel_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
<pointsize>20</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: #0e7077</string>
</property>
<property name="text">
<string>Scripts</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="buttonReloadDefaultScripts">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background: #0e7077;
color: #fff;
border-radius: 4px;
font: bold 14pt;
padding: 10px;margin-top:10px</string>
</property>
<property name="text">
<string>Load Default Scripts</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/transform.hpp>
#include <FBXReader.h>
#include <GeometryUtil.h>
@ -177,21 +179,41 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons
extents.minimum *= scale;
extents.maximum *= scale;
calculateRotatedExtents(extents, model.getModelRotation());
extents.minimum += model.getPosition();
extents.maximum += model.getPosition();
AABox rotatedExtentsBox(extents.minimum, (extents.maximum - extents.minimum));
Extents rotatedExtents = extents;
calculateRotatedExtents(rotatedExtents, model.getModelRotation());
rotatedExtents.minimum += model.getPosition();
rotatedExtents.maximum += model.getPosition();
AABox rotatedExtentsBox(rotatedExtents.minimum, (rotatedExtents.maximum - rotatedExtents.minimum));
// if it's in our AABOX for our rotated extents, then check to see if it's in our non-AABox
if (rotatedExtentsBox.findRayIntersection(origin, direction, localDistance, localFace)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)(&model);
somethingIntersected = true;
}
// extents is the model relative, scaled, centered extents of the model
glm::mat4 rotation = glm::mat4_cast(model.getModelRotation());
glm::mat4 translation = glm::translate(model.getPosition());
glm::mat4 modelToWorldMatrix = translation * rotation;
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
AABox modelFrameBox(extents.minimum, (extents.maximum - extents.minimum));
glm::vec3 modelFrameOrigin = glm::vec3(worldToModelMatrix * glm::vec4(origin, 1.0f));
glm::vec3 modelFrameDirection = glm::vec3(worldToModelMatrix * glm::vec4(direction, 1.0f));
// 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, localDistance, localFace)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)(&model);
somethingIntersected = true;
}
}
}
} else if (localDistance < distance) {
distance = localDistance;

View file

@ -314,6 +314,18 @@ void ScriptEngine::evaluate() {
}
}
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
QScriptValue result = _engine.evaluate(program, fileName, lineNumber);
bool hasUncaughtException = _engine.hasUncaughtException();
if (hasUncaughtException) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ": " << result.toString();
}
emit evaluationFinished(result, hasUncaughtException);
_engine.clearExceptions();
return result;
}
void ScriptEngine::sendAvatarIdentityPacket() {
if (_isAvatar && _avatarData) {
_avatarData->sendIdentityPacket();

View file

@ -92,6 +92,7 @@ public:
public slots:
void stop();
QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1);
QObject* setInterval(const QScriptValue& function, int intervalMS);
QObject* setTimeout(const QScriptValue& function, int timeoutMS);
void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
@ -107,6 +108,7 @@ signals:
void printedMessage(const QString& message);
void errorMessage(const QString& message);
void runningStateChanged();
void evaluationFinished(QScriptValue result, bool isException);
protected:
QString _scriptContents;