mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-19 13:44:20 +02:00
Merge branch master of upstream
This commit is contained in:
commit
2c3784c7d5
28 changed files with 1664 additions and 350 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
302
examples/locationsMenu.js
Normal 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();
|
20
interface/resources/styles/console.qss
Normal file
20
interface/resources/styles/console.qss
Normal 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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
343
interface/src/ui/ApplicationOverlay.cpp
Normal file
343
interface/src/ui/ApplicationOverlay.cpp
Normal 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;
|
||||
}
|
||||
|
38
interface/src/ui/ApplicationOverlay.h
Normal file
38
interface/src/ui/ApplicationOverlay.h
Normal 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
|
229
interface/src/ui/JSConsole.cpp
Normal file
229
interface/src/ui/JSConsole.cpp
Normal 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;\"><</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();
|
||||
}
|
62
interface/src/ui/JSConsole.h
Normal file
62
interface/src/ui/JSConsole.h
Normal 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
|
|
@ -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() {
|
||||
|
|
|
@ -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
258
interface/ui/console.ui
Normal 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>></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>
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue