mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 11:54:20 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into dependancy_manager
This commit is contained in:
commit
28786721a6
58 changed files with 606 additions and 5116 deletions
|
@ -22,6 +22,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <ShutdownEventListener.h>
|
||||
#include <SoundCache.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
|
@ -38,7 +39,6 @@ int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
|||
|
||||
AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
||||
QCoreApplication(argc, argv),
|
||||
_shutdownEventListener(this),
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME),
|
||||
_localASPortSharedMem(NULL)
|
||||
{
|
||||
|
@ -49,8 +49,12 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
setApplicationName("assignment-client");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
installNativeEventFilter(&_shutdownEventListener);
|
||||
connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit()));
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
installNativeEventFilter(&ShutdownEventListener::getInstance());
|
||||
#else
|
||||
ShutdownEventListener::getInstance();
|
||||
#endif
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "ShutdownEventListener.h"
|
||||
#include "ThreadedAssignment.h"
|
||||
|
||||
class QSharedMemory;
|
||||
|
@ -34,7 +33,6 @@ private slots:
|
|||
private:
|
||||
Assignment _requestAssignment;
|
||||
static SharedAssignmentPointer _currentAssignment;
|
||||
ShutdownEventListener _shutdownEventListener;
|
||||
QString _assignmentServerHostname;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
QSharedMemory* _localASPortSharedMem;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <signal.h>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <ShutdownEventListener.h>
|
||||
|
||||
#include "AssignmentClientMonitor.h"
|
||||
|
||||
|
@ -19,24 +20,19 @@ const char* NUM_FORKS_PARAMETER = "-n";
|
|||
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
|
||||
void signalHandler(int param){
|
||||
// get the qApp and cast it to an AssignmentClientMonitor
|
||||
AssignmentClientMonitor* app = qobject_cast<AssignmentClientMonitor*>(qApp);
|
||||
|
||||
// tell it to stop the child processes and then go down
|
||||
app->stopChildProcesses();
|
||||
app->quit();
|
||||
}
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) :
|
||||
QCoreApplication(argc, argv)
|
||||
{
|
||||
// be a signal handler for SIGTERM so we can stop our children when we get it
|
||||
signal(SIGTERM, signalHandler);
|
||||
|
||||
{
|
||||
// start the Logging class with the parent's target name
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
||||
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
installNativeEventFilter(&ShutdownEventListener::getInstance());
|
||||
#else
|
||||
ShutdownEventListener::getInstance();
|
||||
#endif
|
||||
|
||||
_childArguments = arguments();
|
||||
|
||||
// remove the parameter for the number of forks so it isn't passed to the child forked processes
|
||||
|
@ -52,6 +48,10 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int num
|
|||
}
|
||||
}
|
||||
|
||||
AssignmentClientMonitor::~AssignmentClientMonitor() {
|
||||
stopChildProcesses();
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::stopChildProcesses() {
|
||||
|
||||
QList<QPointer<QProcess> >::Iterator it = _childProcesses.begin();
|
||||
|
|
|
@ -24,6 +24,7 @@ class AssignmentClientMonitor : public QCoreApplication {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks);
|
||||
~AssignmentClientMonitor();
|
||||
|
||||
void stopChildProcesses();
|
||||
private slots:
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <LogUtils.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <ShutdownEventListener.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
@ -41,7 +42,6 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
|
|||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_shutdownEventListener(this),
|
||||
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_httpsManager(NULL),
|
||||
_allAssignments(),
|
||||
|
@ -70,8 +70,12 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
_settingsManager.setupConfigMap(arguments());
|
||||
|
||||
installNativeEventFilter(&_shutdownEventListener);
|
||||
connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit()));
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
installNativeEventFilter(&ShutdownEventListener::getInstance());
|
||||
#else
|
||||
ShutdownEventListener::getInstance();
|
||||
#endif
|
||||
|
||||
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
#include "DomainServerSettingsManager.h"
|
||||
#include "DomainServerWebSessionData.h"
|
||||
#include "ShutdownEventListener.h"
|
||||
#include "WalletTransaction.h"
|
||||
|
||||
#include "PendingAssignedNodeData.h"
|
||||
|
@ -125,8 +124,6 @@ private:
|
|||
|
||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||
|
||||
ShutdownEventListener _shutdownEventListener;
|
||||
|
||||
HTTPManager _httpManager;
|
||||
HTTPSManager* _httpsManager;
|
||||
|
|
|
@ -1,844 +0,0 @@
|
|||
//
|
||||
// audioReflectorTools.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/14/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Tools for manipulating the attributes of the AudioReflector behavior
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("libraries/globals.js");
|
||||
|
||||
var delayScale = 100.0;
|
||||
var fanoutScale = 10.0;
|
||||
var speedScale = 20;
|
||||
var factorScale = 5.0;
|
||||
var localFactorScale = 1.0;
|
||||
var reflectiveScale = 100.0;
|
||||
var diffusionScale = 100.0;
|
||||
var absorptionScale = 100.0;
|
||||
var combFilterScale = 50.0;
|
||||
var originalScale = 2.0;
|
||||
var echoesScale = 2.0;
|
||||
|
||||
// these three properties are bound together, if you change one, the others will also change
|
||||
var reflectiveRatio = AudioReflector.getReflectiveRatio();
|
||||
var diffusionRatio = AudioReflector.getDiffusionRatio();
|
||||
var absorptionRatio = AudioReflector.getAbsorptionRatio();
|
||||
|
||||
var reflectiveThumbX;
|
||||
var diffusionThumbX;
|
||||
var absorptionThumbX;
|
||||
|
||||
function setReflectiveRatio(reflective) {
|
||||
var total = diffusionRatio + absorptionRatio + (reflective / reflectiveScale);
|
||||
diffusionRatio = diffusionRatio / total;
|
||||
absorptionRatio = absorptionRatio / total;
|
||||
reflectiveRatio = (reflective / reflectiveScale) / total;
|
||||
updateRatioValues();
|
||||
}
|
||||
|
||||
function setDiffusionRatio(diffusion) {
|
||||
var total = (diffusion / diffusionScale) + absorptionRatio + reflectiveRatio;
|
||||
diffusionRatio = (diffusion / diffusionScale) / total;
|
||||
absorptionRatio = absorptionRatio / total;
|
||||
reflectiveRatio = reflectiveRatio / total;
|
||||
updateRatioValues();
|
||||
}
|
||||
|
||||
function setAbsorptionRatio(absorption) {
|
||||
var total = diffusionRatio + (absorption / absorptionScale) + reflectiveRatio;
|
||||
diffusionRatio = diffusionRatio / total;
|
||||
absorptionRatio = (absorption / absorptionScale) / total;
|
||||
reflectiveRatio = reflectiveRatio / total;
|
||||
updateRatioValues();
|
||||
}
|
||||
|
||||
function updateRatioSliders() {
|
||||
reflectiveThumbX = reflectiveMinThumbX + ((reflectiveMaxThumbX - reflectiveMinThumbX) * reflectiveRatio);
|
||||
diffusionThumbX = diffusionMinThumbX + ((diffusionMaxThumbX - diffusionMinThumbX) * diffusionRatio);
|
||||
absorptionThumbX = absorptionMinThumbX + ((absorptionMaxThumbX - absorptionMinThumbX) * absorptionRatio);
|
||||
|
||||
Overlays.editOverlay(reflectiveThumb, { x: reflectiveThumbX } );
|
||||
Overlays.editOverlay(diffusionThumb, { x: diffusionThumbX } );
|
||||
Overlays.editOverlay(absorptionThumb, { x: absorptionThumbX } );
|
||||
}
|
||||
|
||||
function updateRatioValues() {
|
||||
AudioReflector.setReflectiveRatio(reflectiveRatio);
|
||||
AudioReflector.setDiffusionRatio(diffusionRatio);
|
||||
AudioReflector.setAbsorptionRatio(absorptionRatio);
|
||||
}
|
||||
|
||||
var topY = 250;
|
||||
var sliderHeight = 35;
|
||||
|
||||
var delayY = topY;
|
||||
topY += sliderHeight;
|
||||
var delayLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: delayY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 12,
|
||||
leftMargin: 5,
|
||||
text: "Delay:"
|
||||
});
|
||||
|
||||
var delaySlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: delayY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var delayMinThumbX = 110;
|
||||
var delayMaxThumbX = delayMinThumbX + 110;
|
||||
var delayThumbX = delayMinThumbX + ((delayMaxThumbX - delayMinThumbX) * (AudioReflector.getPreDelay() / delayScale));
|
||||
var delayThumb = Overlays.addOverlay("image", {
|
||||
x: delayThumbX,
|
||||
y: delayY + 9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 255, green: 0, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var fanoutY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var fanoutLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: fanoutY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 12,
|
||||
leftMargin: 5,
|
||||
text: "Fanout:"
|
||||
});
|
||||
|
||||
var fanoutSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: fanoutY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var fanoutMinThumbX = 110;
|
||||
var fanoutMaxThumbX = fanoutMinThumbX + 110;
|
||||
var fanoutThumbX = fanoutMinThumbX + ((fanoutMaxThumbX - fanoutMinThumbX) * (AudioReflector.getDiffusionFanout() / fanoutScale));
|
||||
var fanoutThumb = Overlays.addOverlay("image", {
|
||||
x: fanoutThumbX,
|
||||
y: fanoutY + 9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 255, green: 255, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var speedY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var speedLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: speedY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Speed\nin ms/m:"
|
||||
});
|
||||
|
||||
var speedSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: speedY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var speedMinThumbX = 110;
|
||||
var speedMaxThumbX = speedMinThumbX + 110;
|
||||
var speedThumbX = speedMinThumbX + ((speedMaxThumbX - speedMinThumbX) * (AudioReflector.getSoundMsPerMeter() / speedScale));
|
||||
var speedThumb = Overlays.addOverlay("image", {
|
||||
x: speedThumbX,
|
||||
y: speedY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 0, green: 255, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var factorY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var factorLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: factorY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Attenuation\nFactor:"
|
||||
});
|
||||
|
||||
|
||||
var factorSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: factorY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var factorMinThumbX = 110;
|
||||
var factorMaxThumbX = factorMinThumbX + 110;
|
||||
var factorThumbX = factorMinThumbX + ((factorMaxThumbX - factorMinThumbX) * (AudioReflector.getDistanceAttenuationScalingFactor() / factorScale));
|
||||
var factorThumb = Overlays.addOverlay("image", {
|
||||
x: factorThumbX,
|
||||
y: factorY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 0, green: 0, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var localFactorY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var localFactorLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: localFactorY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Local\nFactor:"
|
||||
});
|
||||
|
||||
|
||||
var localFactorSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: localFactorY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var localFactorMinThumbX = 110;
|
||||
var localFactorMaxThumbX = localFactorMinThumbX + 110;
|
||||
var localFactorThumbX = localFactorMinThumbX + ((localFactorMaxThumbX - localFactorMinThumbX) * (AudioReflector.getLocalAudioAttenuationFactor() / localFactorScale));
|
||||
var localFactorThumb = Overlays.addOverlay("image", {
|
||||
x: localFactorThumbX,
|
||||
y: localFactorY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 0, green: 128, blue: 128},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var combFilterY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var combFilterLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: combFilterY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Comb Filter\nWindow:"
|
||||
});
|
||||
|
||||
|
||||
var combFilterSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: combFilterY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var combFilterMinThumbX = 110;
|
||||
var combFilterMaxThumbX = combFilterMinThumbX + 110;
|
||||
var combFilterThumbX = combFilterMinThumbX + ((combFilterMaxThumbX - combFilterMinThumbX) * (AudioReflector.getCombFilterWindow() / combFilterScale));
|
||||
var combFilterThumb = Overlays.addOverlay("image", {
|
||||
x: combFilterThumbX,
|
||||
y: combFilterY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 128, green: 128, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var reflectiveY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var reflectiveLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: reflectiveY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Reflective\nRatio:"
|
||||
});
|
||||
|
||||
|
||||
var reflectiveSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: reflectiveY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var reflectiveMinThumbX = 110;
|
||||
var reflectiveMaxThumbX = reflectiveMinThumbX + 110;
|
||||
reflectiveThumbX = reflectiveMinThumbX + ((reflectiveMaxThumbX - reflectiveMinThumbX) * AudioReflector.getReflectiveRatio());
|
||||
var reflectiveThumb = Overlays.addOverlay("image", {
|
||||
x: reflectiveThumbX,
|
||||
y: reflectiveY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var diffusionY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var diffusionLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: diffusionY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Diffusion\nRatio:"
|
||||
});
|
||||
|
||||
|
||||
var diffusionSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: diffusionY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var diffusionMinThumbX = 110;
|
||||
var diffusionMaxThumbX = diffusionMinThumbX + 110;
|
||||
diffusionThumbX = diffusionMinThumbX + ((diffusionMaxThumbX - diffusionMinThumbX) * AudioReflector.getDiffusionRatio());
|
||||
var diffusionThumb = Overlays.addOverlay("image", {
|
||||
x: diffusionThumbX,
|
||||
y: diffusionY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 0, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var absorptionY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var absorptionLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: absorptionY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Absorption\nRatio:"
|
||||
});
|
||||
|
||||
|
||||
var absorptionSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: absorptionY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var absorptionMinThumbX = 110;
|
||||
var absorptionMaxThumbX = absorptionMinThumbX + 110;
|
||||
absorptionThumbX = absorptionMinThumbX + ((absorptionMaxThumbX - absorptionMinThumbX) * AudioReflector.getAbsorptionRatio());
|
||||
var absorptionThumb = Overlays.addOverlay("image", {
|
||||
x: absorptionThumbX,
|
||||
y: absorptionY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 255, green: 0, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var originalY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var originalLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: originalY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Original\nMix:"
|
||||
});
|
||||
|
||||
|
||||
var originalSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: originalY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var originalMinThumbX = 110;
|
||||
var originalMaxThumbX = originalMinThumbX + 110;
|
||||
var originalThumbX = originalMinThumbX + ((originalMaxThumbX - originalMinThumbX) * (AudioReflector.getOriginalSourceAttenuation() / originalScale));
|
||||
var originalThumb = Overlays.addOverlay("image", {
|
||||
x: originalThumbX,
|
||||
y: originalY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 128, green: 128, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var echoesY = topY;
|
||||
topY += sliderHeight;
|
||||
|
||||
var echoesLabel = Overlays.addOverlay("text", {
|
||||
x: 40,
|
||||
y: echoesY,
|
||||
width: 60,
|
||||
height: sliderHeight,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 255, blue: 255},
|
||||
topMargin: 6,
|
||||
leftMargin: 5,
|
||||
text: "Echoes\nMix:"
|
||||
});
|
||||
|
||||
|
||||
var echoesSlider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: 100, y: echoesY, width: 150, height: sliderHeight},
|
||||
subImage: { x: 46, y: 0, width: 200, height: 71 },
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
var echoesMinThumbX = 110;
|
||||
var echoesMaxThumbX = echoesMinThumbX + 110;
|
||||
var echoesThumbX = echoesMinThumbX + ((echoesMaxThumbX - echoesMinThumbX) * (AudioReflector.getEchoesAttenuation() / echoesScale));
|
||||
var echoesThumb = Overlays.addOverlay("image", {
|
||||
x: echoesThumbX,
|
||||
y: echoesY+9,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/thumb.png",
|
||||
color: { red: 128, green: 128, blue: 0},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(factorLabel);
|
||||
Overlays.deleteOverlay(factorThumb);
|
||||
Overlays.deleteOverlay(factorSlider);
|
||||
|
||||
Overlays.deleteOverlay(combFilterLabel);
|
||||
Overlays.deleteOverlay(combFilterThumb);
|
||||
Overlays.deleteOverlay(combFilterSlider);
|
||||
|
||||
Overlays.deleteOverlay(localFactorLabel);
|
||||
Overlays.deleteOverlay(localFactorThumb);
|
||||
Overlays.deleteOverlay(localFactorSlider);
|
||||
|
||||
Overlays.deleteOverlay(speedLabel);
|
||||
Overlays.deleteOverlay(speedThumb);
|
||||
Overlays.deleteOverlay(speedSlider);
|
||||
|
||||
Overlays.deleteOverlay(delayLabel);
|
||||
Overlays.deleteOverlay(delayThumb);
|
||||
Overlays.deleteOverlay(delaySlider);
|
||||
|
||||
Overlays.deleteOverlay(fanoutLabel);
|
||||
Overlays.deleteOverlay(fanoutThumb);
|
||||
Overlays.deleteOverlay(fanoutSlider);
|
||||
|
||||
Overlays.deleteOverlay(reflectiveLabel);
|
||||
Overlays.deleteOverlay(reflectiveThumb);
|
||||
Overlays.deleteOverlay(reflectiveSlider);
|
||||
|
||||
Overlays.deleteOverlay(diffusionLabel);
|
||||
Overlays.deleteOverlay(diffusionThumb);
|
||||
Overlays.deleteOverlay(diffusionSlider);
|
||||
|
||||
Overlays.deleteOverlay(absorptionLabel);
|
||||
Overlays.deleteOverlay(absorptionThumb);
|
||||
Overlays.deleteOverlay(absorptionSlider);
|
||||
|
||||
Overlays.deleteOverlay(echoesLabel);
|
||||
Overlays.deleteOverlay(echoesThumb);
|
||||
Overlays.deleteOverlay(echoesSlider);
|
||||
|
||||
Overlays.deleteOverlay(originalLabel);
|
||||
Overlays.deleteOverlay(originalThumb);
|
||||
Overlays.deleteOverlay(originalSlider);
|
||||
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
var count = 0;
|
||||
|
||||
// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays
|
||||
function update(deltaTime) {
|
||||
count++;
|
||||
}
|
||||
Script.update.connect(update);
|
||||
|
||||
|
||||
// The slider is handled in the mouse event callbacks.
|
||||
var movingSliderDelay = false;
|
||||
var movingSliderFanout = false;
|
||||
var movingSliderSpeed = false;
|
||||
var movingSliderFactor = false;
|
||||
var movingSliderCombFilter = false;
|
||||
var movingSliderLocalFactor = false;
|
||||
var movingSliderReflective = false;
|
||||
var movingSliderDiffusion = false;
|
||||
var movingSliderAbsorption = false;
|
||||
var movingSliderOriginal = false;
|
||||
var movingSliderEchoes = false;
|
||||
|
||||
var thumbClickOffsetX = 0;
|
||||
function mouseMoveEvent(event) {
|
||||
if (movingSliderDelay) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < delayMinThumbX) {
|
||||
newThumbX = delayMinThumbX;
|
||||
}
|
||||
if (newThumbX > delayMaxThumbX) {
|
||||
newThumbX = delayMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(delayThumb, { x: newThumbX } );
|
||||
var delay = ((newThumbX - delayMinThumbX) / (delayMaxThumbX - delayMinThumbX)) * delayScale;
|
||||
AudioReflector.setPreDelay(delay);
|
||||
}
|
||||
if (movingSliderFanout) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < fanoutMinThumbX) {
|
||||
newThumbX = fanoutMinThumbX;
|
||||
}
|
||||
if (newThumbX > fanoutMaxThumbX) {
|
||||
newThumbX = fanoutMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(fanoutThumb, { x: newThumbX } );
|
||||
var fanout = Math.round(((newThumbX - fanoutMinThumbX) / (fanoutMaxThumbX - fanoutMinThumbX)) * fanoutScale);
|
||||
AudioReflector.setDiffusionFanout(fanout);
|
||||
}
|
||||
if (movingSliderSpeed) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < speedMinThumbX) {
|
||||
newThumbX = speedMminThumbX;
|
||||
}
|
||||
if (newThumbX > speedMaxThumbX) {
|
||||
newThumbX = speedMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(speedThumb, { x: newThumbX } );
|
||||
var speed = ((newThumbX - speedMinThumbX) / (speedMaxThumbX - speedMinThumbX)) * speedScale;
|
||||
AudioReflector.setSoundMsPerMeter(speed);
|
||||
}
|
||||
if (movingSliderFactor) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < factorMinThumbX) {
|
||||
newThumbX = factorMminThumbX;
|
||||
}
|
||||
if (newThumbX > factorMaxThumbX) {
|
||||
newThumbX = factorMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(factorThumb, { x: newThumbX } );
|
||||
var factor = ((newThumbX - factorMinThumbX) / (factorMaxThumbX - factorMinThumbX)) * factorScale;
|
||||
AudioReflector.setDistanceAttenuationScalingFactor(factor);
|
||||
}
|
||||
if (movingSliderCombFilter) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < combFilterMinThumbX) {
|
||||
newThumbX = combFilterMminThumbX;
|
||||
}
|
||||
if (newThumbX > combFilterMaxThumbX) {
|
||||
newThumbX = combFilterMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(combFilterThumb, { x: newThumbX } );
|
||||
var combFilter = ((newThumbX - combFilterMinThumbX) / (combFilterMaxThumbX - combFilterMinThumbX)) * combFilterScale;
|
||||
AudioReflector.setCombFilterWindow(combFilter);
|
||||
}
|
||||
if (movingSliderLocalFactor) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < localFactorMinThumbX) {
|
||||
newThumbX = localFactorMminThumbX;
|
||||
}
|
||||
if (newThumbX > localFactorMaxThumbX) {
|
||||
newThumbX = localFactorMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(localFactorThumb, { x: newThumbX } );
|
||||
var localFactor = ((newThumbX - localFactorMinThumbX) / (localFactorMaxThumbX - localFactorMinThumbX)) * localFactorScale;
|
||||
AudioReflector.setLocalAudioAttenuationFactor(localFactor);
|
||||
}
|
||||
|
||||
if (movingSliderAbsorption) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < absorptionMinThumbX) {
|
||||
newThumbX = absorptionMminThumbX;
|
||||
}
|
||||
if (newThumbX > absorptionMaxThumbX) {
|
||||
newThumbX = absorptionMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(absorptionThumb, { x: newThumbX } );
|
||||
var absorption = ((newThumbX - absorptionMinThumbX) / (absorptionMaxThumbX - absorptionMinThumbX)) * absorptionScale;
|
||||
setAbsorptionRatio(absorption);
|
||||
}
|
||||
|
||||
if (movingSliderReflective) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < reflectiveMinThumbX) {
|
||||
newThumbX = reflectiveMminThumbX;
|
||||
}
|
||||
if (newThumbX > reflectiveMaxThumbX) {
|
||||
newThumbX = reflectiveMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(reflectiveThumb, { x: newThumbX } );
|
||||
var reflective = ((newThumbX - reflectiveMinThumbX) / (reflectiveMaxThumbX - reflectiveMinThumbX)) * reflectiveScale;
|
||||
setReflectiveRatio(reflective);
|
||||
}
|
||||
|
||||
if (movingSliderDiffusion) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < diffusionMinThumbX) {
|
||||
newThumbX = diffusionMminThumbX;
|
||||
}
|
||||
if (newThumbX > diffusionMaxThumbX) {
|
||||
newThumbX = diffusionMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(diffusionThumb, { x: newThumbX } );
|
||||
var diffusion = ((newThumbX - diffusionMinThumbX) / (diffusionMaxThumbX - diffusionMinThumbX)) * diffusionScale;
|
||||
setDiffusionRatio(diffusion);
|
||||
}
|
||||
if (movingSliderEchoes) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < echoesMinThumbX) {
|
||||
newThumbX = echoesMminThumbX;
|
||||
}
|
||||
if (newThumbX > echoesMaxThumbX) {
|
||||
newThumbX = echoesMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(echoesThumb, { x: newThumbX } );
|
||||
var echoes = ((newThumbX - echoesMinThumbX) / (echoesMaxThumbX - echoesMinThumbX)) * echoesScale;
|
||||
AudioReflector.setEchoesAttenuation(echoes);
|
||||
}
|
||||
if (movingSliderOriginal) {
|
||||
newThumbX = event.x - thumbClickOffsetX;
|
||||
if (newThumbX < originalMinThumbX) {
|
||||
newThumbX = originalMminThumbX;
|
||||
}
|
||||
if (newThumbX > originalMaxThumbX) {
|
||||
newThumbX = originalMaxThumbX;
|
||||
}
|
||||
Overlays.editOverlay(originalThumb, { x: newThumbX } );
|
||||
var original = ((newThumbX - originalMinThumbX) / (originalMaxThumbX - originalMinThumbX)) * originalScale;
|
||||
AudioReflector.setOriginalSourceAttenuation(original);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// we also handle click detection in our mousePressEvent()
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
if (clickedOverlay == delayThumb) {
|
||||
movingSliderDelay = true;
|
||||
thumbClickOffsetX = event.x - delayThumbX;
|
||||
}
|
||||
if (clickedOverlay == fanoutThumb) {
|
||||
movingSliderFanout = true;
|
||||
thumbClickOffsetX = event.x - fanoutThumbX;
|
||||
}
|
||||
if (clickedOverlay == speedThumb) {
|
||||
movingSliderSpeed = true;
|
||||
thumbClickOffsetX = event.x - speedThumbX;
|
||||
}
|
||||
if (clickedOverlay == factorThumb) {
|
||||
movingSliderFactor = true;
|
||||
thumbClickOffsetX = event.x - factorThumbX;
|
||||
}
|
||||
if (clickedOverlay == localFactorThumb) {
|
||||
movingSliderLocalFactor = true;
|
||||
thumbClickOffsetX = event.x - localFactorThumbX;
|
||||
}
|
||||
if (clickedOverlay == combFilterThumb) {
|
||||
movingSliderCombFilter = true;
|
||||
thumbClickOffsetX = event.x - combFilterThumbX;
|
||||
}
|
||||
if (clickedOverlay == diffusionThumb) {
|
||||
movingSliderDiffusion = true;
|
||||
thumbClickOffsetX = event.x - diffusionThumbX;
|
||||
}
|
||||
if (clickedOverlay == absorptionThumb) {
|
||||
movingSliderAbsorption = true;
|
||||
thumbClickOffsetX = event.x - absorptionThumbX;
|
||||
}
|
||||
if (clickedOverlay == reflectiveThumb) {
|
||||
movingSliderReflective = true;
|
||||
thumbClickOffsetX = event.x - reflectiveThumbX;
|
||||
}
|
||||
if (clickedOverlay == originalThumb) {
|
||||
movingSliderOriginal = true;
|
||||
thumbClickOffsetX = event.x - originalThumbX;
|
||||
}
|
||||
if (clickedOverlay == echoesThumb) {
|
||||
movingSliderEchoes = true;
|
||||
thumbClickOffsetX = event.x - echoesThumbX;
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (movingSliderDelay) {
|
||||
movingSliderDelay = false;
|
||||
var delay = ((newThumbX - delayMinThumbX) / (delayMaxThumbX - delayMinThumbX)) * delayScale;
|
||||
AudioReflector.setPreDelay(delay);
|
||||
delayThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderFanout) {
|
||||
movingSliderFanout = false;
|
||||
var fanout = Math.round(((newThumbX - fanoutMinThumbX) / (fanoutMaxThumbX - fanoutMinThumbX)) * fanoutScale);
|
||||
AudioReflector.setDiffusionFanout(fanout);
|
||||
fanoutThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderSpeed) {
|
||||
movingSliderSpeed = false;
|
||||
var speed = ((newThumbX - speedMinThumbX) / (speedMaxThumbX - speedMinThumbX)) * speedScale;
|
||||
AudioReflector.setSoundMsPerMeter(speed);
|
||||
speedThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderFactor) {
|
||||
movingSliderFactor = false;
|
||||
var factor = ((newThumbX - factorMinThumbX) / (factorMaxThumbX - factorMinThumbX)) * factorScale;
|
||||
AudioReflector.setDistanceAttenuationScalingFactor(factor);
|
||||
factorThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderCombFilter) {
|
||||
movingSliderCombFilter = false;
|
||||
var combFilter = ((newThumbX - combFilterMinThumbX) / (combFilterMaxThumbX - combFilterMinThumbX)) * combFilterScale;
|
||||
AudioReflector.setCombFilterWindow(combFilter);
|
||||
combFilterThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderLocalFactor) {
|
||||
movingSliderLocalFactor = false;
|
||||
var localFactor = ((newThumbX - localFactorMinThumbX) / (localFactorMaxThumbX - localFactorMinThumbX)) * localFactorScale;
|
||||
AudioReflector.setLocalAudioAttenuationFactor(localFactor);
|
||||
localFactorThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderReflective) {
|
||||
movingSliderReflective = false;
|
||||
var reflective = ((newThumbX - reflectiveMinThumbX) / (reflectiveMaxThumbX - reflectiveMinThumbX)) * reflectiveScale;
|
||||
setReflectiveRatio(reflective);
|
||||
reflectiveThumbX = newThumbX;
|
||||
updateRatioSliders();
|
||||
}
|
||||
if (movingSliderDiffusion) {
|
||||
movingSliderDiffusion = false;
|
||||
var diffusion = ((newThumbX - diffusionMinThumbX) / (diffusionMaxThumbX - diffusionMinThumbX)) * diffusionScale;
|
||||
setDiffusionRatio(diffusion);
|
||||
diffusionThumbX = newThumbX;
|
||||
updateRatioSliders();
|
||||
}
|
||||
if (movingSliderAbsorption) {
|
||||
movingSliderAbsorption = false;
|
||||
var absorption = ((newThumbX - absorptionMinThumbX) / (absorptionMaxThumbX - absorptionMinThumbX)) * absorptionScale;
|
||||
setAbsorptionRatio(absorption);
|
||||
absorptionThumbX = newThumbX;
|
||||
updateRatioSliders();
|
||||
}
|
||||
if (movingSliderEchoes) {
|
||||
movingSliderEchoes = false;
|
||||
var echoes = ((newThumbX - echoesMinThumbX) / (echoesMaxThumbX - echoesMinThumbX)) * echoesScale;
|
||||
AudioReflector.setEchoesAttenuation(echoes);
|
||||
echoesThumbX = newThumbX;
|
||||
}
|
||||
if (movingSliderOriginal) {
|
||||
movingSliderOriginal = false;
|
||||
var original = ((newThumbX - originalMinThumbX) / (originalMaxThumbX - originalMinThumbX)) * originalScale;
|
||||
AudioReflector.setOriginalSourceAttenuation(original);
|
||||
originalThumbX = newThumbX;
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
|
@ -14,3 +14,4 @@ Script.load("selectAudioDevice.js");
|
|||
Script.load("hydraMove.js");
|
||||
Script.load("headMove.js");
|
||||
Script.load("inspect.js");
|
||||
Script.load("lobby.js");
|
||||
|
|
|
@ -159,8 +159,8 @@ var toolBar = (function () {
|
|||
visible: false
|
||||
});
|
||||
|
||||
menuItemWidth = Math.max(Overlays.textWidth(loadURLMenuItem, "Model URL"),
|
||||
Overlays.textWidth(loadFileMenuItem, "Model File")) + 20;
|
||||
menuItemWidth = Math.max(Overlays.textSize(loadURLMenuItem, "Model URL").width,
|
||||
Overlays.textSize(loadFileMenuItem, "Model File").width) + 20;
|
||||
Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth });
|
||||
Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth });
|
||||
|
||||
|
|
|
@ -1191,8 +1191,8 @@ var toolBar = (function () {
|
|||
visible: false
|
||||
});
|
||||
|
||||
menuItemWidth = Math.max(Overlays.textWidth(loadURLMenuItem, "Model URL"),
|
||||
Overlays.textWidth(loadFileMenuItem, "Model File")) + 20;
|
||||
menuItemWidth = Math.max(Overlays.textSize(loadURLMenuItem, "Model URL").width,
|
||||
Overlays.textSize(loadFileMenuItem, "Model File").width) + 20;
|
||||
Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth });
|
||||
Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth });
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8};
|
|||
|
||||
var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx"
|
||||
|
||||
var LOBBY_PANEL_WALL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/PanelWallForInterface.fbx";
|
||||
var LOBBY_BLANK_PANEL_TEXTURE_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Texture.jpg";
|
||||
var LOBBY_SHELL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyShellForInterface.fbx";
|
||||
|
||||
var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.stereo.raw")
|
||||
var currentDrone = null;
|
||||
|
||||
|
@ -62,6 +66,21 @@ function textOverlayPosition() {
|
|||
Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN));
|
||||
}
|
||||
|
||||
var panelLocationOrder = [
|
||||
7, 8, 9, 10, 11, 12, 13,
|
||||
0, 1, 2, 3, 4, 5, 6,
|
||||
14, 15, 16, 17, 18, 19, 20
|
||||
];
|
||||
|
||||
// Location index is 0-based
|
||||
function locationIndexToPanelIndex(locationIndex) {
|
||||
return panelLocationOrder.indexOf(locationIndex) + 1;
|
||||
}
|
||||
|
||||
// Panel index is 1-based
|
||||
function panelIndexToLocationIndex(panelIndex) {
|
||||
return panelLocationOrder[panelIndex - 1];
|
||||
}
|
||||
|
||||
var MAX_NUM_PANELS = 21;
|
||||
var DRONE_VOLUME = 0.3;
|
||||
|
@ -76,14 +95,14 @@ function drawLobby() {
|
|||
var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT));
|
||||
|
||||
var panelWallProps = {
|
||||
url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/PanelWall2.fbx",
|
||||
url: LOBBY_PANEL_WALL_URL,
|
||||
position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)),
|
||||
rotation: towardsMe,
|
||||
dimensions: panelsDimensions
|
||||
};
|
||||
|
||||
var orbShellProps = {
|
||||
url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/LobbyShell1.4_LightTag.fbx",
|
||||
url: LOBBY_SHELL_URL,
|
||||
position: orbPosition,
|
||||
rotation: towardsMe,
|
||||
dimensions: orbDimensions,
|
||||
|
@ -143,7 +162,9 @@ function changeLobbyTextures() {
|
|||
};
|
||||
|
||||
for (var j = 0; j < NUM_PANELS; j++) {
|
||||
textureProp["textures"]["file" + (j + 1)] = "http:" + locations[j].thumbnail_url
|
||||
var panelIndex = locationIndexToPanelIndex(j);
|
||||
textureProp["textures"]["file" + panelIndex] = HIFI_PUBLIC_BUCKET + "images/locations/"
|
||||
+ locations[j].id + "/hifi-location-" + locations[j].id + "_640x360.jpg";
|
||||
};
|
||||
|
||||
Overlays.editOverlay(panelWall, textureProp);
|
||||
|
@ -193,7 +214,7 @@ function cleanupLobby() {
|
|||
panelTexturesReset["textures"] = {};
|
||||
|
||||
for (var j = 0; j < MAX_NUM_PANELS; j++) {
|
||||
panelTexturesReset["textures"]["file" + (j + 1)] = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Texture.jpg";
|
||||
panelTexturesReset["textures"]["file" + (j + 1)] = LOBBY_BLANK_PANEL_TEXTURE_URL;
|
||||
};
|
||||
|
||||
Overlays.editOverlay(panelWall, panelTexturesReset);
|
||||
|
@ -228,12 +249,13 @@ function actionStartEvent(event) {
|
|||
|
||||
var panelStringIndex = panelName.indexOf("Panel");
|
||||
if (panelStringIndex != -1) {
|
||||
var panelIndex = parseInt(panelName.slice(5)) - 1;
|
||||
if (panelIndex < locations.length) {
|
||||
var actionLocation = locations[panelIndex];
|
||||
var panelIndex = parseInt(panelName.slice(5));
|
||||
var locationIndex = panelIndexToLocationIndex(panelIndex);
|
||||
if (locationIndex < locations.length) {
|
||||
var actionLocation = locations[locationIndex];
|
||||
|
||||
print("Jumping to " + actionLocation.name + " at " + actionLocation.path
|
||||
+ " in " + actionLocation.domain.name + " after click on panel " + panelIndex);
|
||||
+ " in " + actionLocation.domain.name + " after click on panel " + panelIndex + " with location index " + locationIndex);
|
||||
|
||||
Window.location = actionLocation;
|
||||
maybeCleanupLobby();
|
||||
|
@ -277,9 +299,10 @@ function handleLookAt(pickRay) {
|
|||
var panelName = result.extraInfo;
|
||||
var panelStringIndex = panelName.indexOf("Panel");
|
||||
if (panelStringIndex != -1) {
|
||||
var panelIndex = parseInt(panelName.slice(5)) - 1;
|
||||
if (panelIndex < locations.length) {
|
||||
var actionLocation = locations[panelIndex];
|
||||
var panelIndex = parseInt(panelName.slice(5));
|
||||
var locationIndex = panelIndexToLocationIndex(panelIndex);
|
||||
if (locationIndex < locations.length) {
|
||||
var actionLocation = locations[locationIndex];
|
||||
|
||||
if (actionLocation.description == "") {
|
||||
Overlays.editOverlay(descriptionText, { text: actionLocation.name, visible: showText });
|
||||
|
@ -300,7 +323,7 @@ function handleLookAt(pickRay) {
|
|||
} else {
|
||||
currentTestLine = allWords[currentTestWord];
|
||||
}
|
||||
var lineLength = Overlays.textWidth(descriptionText, currentTestLine);
|
||||
var lineLength = Overlays.textSize(descriptionText, currentTestLine).width;
|
||||
if (lineLength < textWidth || wordsOnLine == 0) {
|
||||
wordsFormated++;
|
||||
currentTestWord++;
|
||||
|
|
|
@ -86,6 +86,7 @@ ChessGame.Board = (function(position, scale) {
|
|||
modelURL: ChessGame.BOARD.modelURL,
|
||||
position: this.position,
|
||||
dimensions: this.dimensions,
|
||||
rotation: ChessGame.BOARD.rotation,
|
||||
userData: this.buildUserDataString()
|
||||
}
|
||||
this.entity = null;
|
||||
|
|
|
@ -80,7 +80,7 @@ function updateTextOverlay() {
|
|||
var textLines = textText.split("\n");
|
||||
var maxLineWidth = 0;
|
||||
for (textLine in textLines) {
|
||||
var lineWidth = Overlays.textWidth(text, textLines[textLine]);
|
||||
var lineWidth = Overlays.textSize(text, textLines[textLine]).width;
|
||||
if (lineWidth > maxLineWidth) {
|
||||
maxLineWidth = lineWidth;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ function updateTextOverlay() {
|
|||
Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin});
|
||||
var maxLineWidth = 0;
|
||||
for (textLine in textLines) {
|
||||
var lineWidth = Overlays.textWidth(text, textLines[textLine]);
|
||||
var lineWidth = Overlays.textSize(text, textLines[textLine]).width;
|
||||
if (lineWidth > maxLineWidth) {
|
||||
maxLineWidth = lineWidth;
|
||||
}
|
||||
|
@ -122,18 +122,18 @@ keyboard.onKeyRelease = function(event) {
|
|||
var textLines = textText.split("\n");
|
||||
var maxLineWidth = 0;
|
||||
for (textLine in textLines) {
|
||||
var lineWidth = Overlays.textWidth(textSizeMeasureOverlay, textLines[textLine]);
|
||||
var lineWidth = Overlays.textSize(textSizeMeasureOverlay, textLines[textLine]).width;
|
||||
if (lineWidth > maxLineWidth) {
|
||||
maxLineWidth = lineWidth;
|
||||
}
|
||||
}
|
||||
var usernameLine = "--" + GlobalServices.myUsername;
|
||||
var usernameWidth = Overlays.textWidth(textSizeMeasureOverlay, usernameLine);
|
||||
var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width;
|
||||
if (maxLineWidth < usernameWidth) {
|
||||
maxLineWidth = usernameWidth;
|
||||
} else {
|
||||
var spaceableWidth = maxLineWidth - usernameWidth;
|
||||
var spaceWidth = Overlays.textWidth(textSizeMeasureOverlay, " ");
|
||||
var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, " ").width;
|
||||
var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth);
|
||||
for (var i = 0; i < numberOfSpaces; i++) {
|
||||
usernameLine = " " + usernameLine;
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// passthrough.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
attribute float voxelSizeIn;
|
||||
varying float voxelSize;
|
||||
|
||||
void main(void) {
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_Position = gl_Vertex; // don't call ftransform(), because we do projection in geometry shader
|
||||
voxelSize = voxelSizeIn;
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// point_size.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
attribute float voxelSizeIn;
|
||||
varying float voxelSize;
|
||||
|
||||
uniform float viewportWidth;
|
||||
uniform float viewportHeight;
|
||||
uniform vec3 cameraPosition;
|
||||
|
||||
// Bit codes for faces
|
||||
const int NONE = 0;
|
||||
const int RIGHT = 1;
|
||||
const int LEFT = 2;
|
||||
const int BOTTOM = 4;
|
||||
const int BOTTOM_RIGHT = BOTTOM + RIGHT;
|
||||
const int BOTTOM_LEFT = BOTTOM + LEFT;
|
||||
const int TOP = 8;
|
||||
const int TOP_RIGHT = TOP + RIGHT;
|
||||
const int TOP_LEFT = TOP + LEFT;
|
||||
const int NEAR = 16;
|
||||
const int NEAR_RIGHT = NEAR + RIGHT;
|
||||
const int NEAR_LEFT = NEAR + LEFT;
|
||||
const int NEAR_BOTTOM = NEAR + BOTTOM;
|
||||
const int NEAR_BOTTOM_RIGHT = NEAR + BOTTOM + RIGHT;
|
||||
const int NEAR_BOTTOM_LEFT = NEAR + BOTTOM + LEFT;
|
||||
const int NEAR_TOP = NEAR + TOP;
|
||||
const int NEAR_TOP_RIGHT = NEAR + TOP + RIGHT;
|
||||
const int NEAR_TOP_LEFT = NEAR + TOP + LEFT;
|
||||
const int FAR = 32;
|
||||
const int FAR_RIGHT = FAR + RIGHT;
|
||||
const int FAR_LEFT = FAR + LEFT;
|
||||
const int FAR_BOTTOM = FAR + BOTTOM;
|
||||
const int FAR_BOTTOM_RIGHT = FAR + BOTTOM + RIGHT;
|
||||
const int FAR_BOTTOM_LEFT = FAR + BOTTOM + LEFT;
|
||||
const int FAR_TOP = FAR + TOP;
|
||||
const int FAR_TOP_RIGHT = FAR + TOP + RIGHT;
|
||||
const int FAR_TOP_LEFT = FAR + TOP + LEFT;
|
||||
|
||||
// If we know the position of the camera relative to the voxel, we can a priori know the vertices that make the visible hull
|
||||
// polygon. This also tells us which two vertices are known to make the longest possible distance between any pair of these
|
||||
// vertices for the projected polygon. This is a visibleFaces table based on this knowledge.
|
||||
|
||||
void main(void) {
|
||||
// Note: the gl_Vertex in this case are in "world coordinates" meaning they've already been scaled to TREE_SCALE
|
||||
// this is also true for voxelSizeIn.
|
||||
vec4 bottomNearRight = gl_Vertex;
|
||||
vec4 topFarLeft = (gl_Vertex + vec4(voxelSizeIn, voxelSizeIn, voxelSizeIn, 0.0));
|
||||
|
||||
int visibleFaces = NONE;
|
||||
|
||||
// In order to use our visibleFaces "table" (implemented as if statements) below, we need to encode the 6-bit code to
|
||||
// orient camera relative to the 6 defining faces of the voxel. Based on camera position relative to the bottomNearRight
|
||||
// corner and the topFarLeft corner, we can calculate which hull and therefore which two vertices are furthest apart
|
||||
// linearly once projected
|
||||
if (cameraPosition.x < bottomNearRight.x) {
|
||||
visibleFaces += RIGHT;
|
||||
}
|
||||
if (cameraPosition.x > topFarLeft.x) {
|
||||
visibleFaces += LEFT;
|
||||
}
|
||||
if (cameraPosition.y < bottomNearRight.y) {
|
||||
visibleFaces += BOTTOM;
|
||||
}
|
||||
if (cameraPosition.y > topFarLeft.y) {
|
||||
visibleFaces += TOP;
|
||||
}
|
||||
if (cameraPosition.z < bottomNearRight.z) {
|
||||
visibleFaces += NEAR;
|
||||
}
|
||||
if (cameraPosition.z > topFarLeft.z) {
|
||||
visibleFaces += FAR;
|
||||
}
|
||||
|
||||
vec4 cornerAdjustOne;
|
||||
vec4 cornerAdjustTwo;
|
||||
|
||||
if (visibleFaces == RIGHT) {
|
||||
cornerAdjustOne = vec4(0,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(0,1,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == LEFT) {
|
||||
cornerAdjustOne = vec4(1,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == BOTTOM) {
|
||||
cornerAdjustOne = vec4(0,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,0,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == TOP) {
|
||||
cornerAdjustOne = vec4(0,1,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == NEAR) {
|
||||
cornerAdjustOne = vec4(0,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,0,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == FAR) {
|
||||
cornerAdjustOne = vec4(0,0,1,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == NEAR_BOTTOM_LEFT ||
|
||||
visibleFaces == FAR_TOP ||
|
||||
visibleFaces == FAR_TOP_RIGHT) {
|
||||
cornerAdjustOne = vec4(0,1,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,0,1,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == FAR_TOP_LEFT ||
|
||||
visibleFaces == NEAR_RIGHT ||
|
||||
visibleFaces == NEAR_BOTTOM ||
|
||||
visibleFaces == NEAR_BOTTOM_RIGHT) {
|
||||
cornerAdjustOne = vec4(0,0,1,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,0,0) * voxelSizeIn;
|
||||
} else if (visibleFaces == NEAR_TOP_RIGHT ||
|
||||
visibleFaces == FAR_LEFT ||
|
||||
visibleFaces == FAR_BOTTOM_LEFT ||
|
||||
visibleFaces == BOTTOM_RIGHT ||
|
||||
visibleFaces == TOP_LEFT) {
|
||||
cornerAdjustOne = vec4(1,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(0,1,1,0) * voxelSizeIn;
|
||||
|
||||
// Everything else...
|
||||
//} else if (visibleFaces == BOTTOM_LEFT ||
|
||||
// visibleFaces == TOP_RIGHT ||
|
||||
// visibleFaces == NEAR_LEFT ||
|
||||
// visibleFaces == FAR_RIGHT ||
|
||||
// visibleFaces == NEAR_TOP ||
|
||||
// visibleFaces == NEAR_TOP_LEFT ||
|
||||
// visibleFaces == FAR_BOTTOM ||
|
||||
// visibleFaces == FAR_BOTTOM_RIGHT) {
|
||||
} else {
|
||||
cornerAdjustOne = vec4(0,0,0,0) * voxelSizeIn;
|
||||
cornerAdjustTwo = vec4(1,1,1,0) * voxelSizeIn;
|
||||
}
|
||||
|
||||
// Determine our corners
|
||||
vec4 cornerOne = gl_Vertex + cornerAdjustOne;
|
||||
vec4 cornerTwo = gl_Vertex + cornerAdjustTwo;
|
||||
|
||||
// Find their model view projections
|
||||
vec4 cornerOneMVP = gl_ModelViewProjectionMatrix * cornerOne;
|
||||
vec4 cornerTwoMVP = gl_ModelViewProjectionMatrix * cornerTwo;
|
||||
|
||||
// Map to x, y screen coordinates
|
||||
vec2 cornerOneScreen = vec2(cornerOneMVP.x / cornerOneMVP.w, cornerOneMVP.y / cornerOneMVP.w);
|
||||
if (cornerOneMVP.w < 0) {
|
||||
cornerOneScreen.x = -cornerOneScreen.x;
|
||||
cornerOneScreen.y = -cornerOneScreen.y;
|
||||
}
|
||||
|
||||
vec2 cornerTwoScreen = vec2(cornerTwoMVP.x / cornerTwoMVP.w, cornerTwoMVP.y / cornerTwoMVP.w);
|
||||
if (cornerTwoMVP.w < 0) {
|
||||
cornerTwoScreen.x = -cornerTwoScreen.x;
|
||||
cornerTwoScreen.y = -cornerTwoScreen.y;
|
||||
}
|
||||
|
||||
// Find the distance between them in pixels
|
||||
float voxelScreenWidth = abs(cornerOneScreen.x - cornerTwoScreen.x) * viewportWidth / 2.0;
|
||||
float voxelScreenHeight = abs(cornerOneScreen.y - cornerTwoScreen.y) * viewportHeight / 2.0;
|
||||
float voxelScreenLength = sqrt(voxelScreenHeight * voxelScreenHeight + voxelScreenWidth * voxelScreenWidth);
|
||||
|
||||
// Find the center of the voxel
|
||||
vec4 centerVertex = gl_Vertex;
|
||||
float halfSizeIn = voxelSizeIn / 2;
|
||||
centerVertex += vec4(halfSizeIn, halfSizeIn, halfSizeIn, 0.0);
|
||||
vec4 center = gl_ModelViewProjectionMatrix * centerVertex;
|
||||
|
||||
// Finally place the point at the center of the voxel, with a size equal to the maximum screen length
|
||||
gl_Position = center;
|
||||
gl_PointSize = voxelScreenLength;
|
||||
gl_FrontColor = gl_Color;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
#version 120
|
||||
#extension GL_ARB_geometry_shader4 : enable
|
||||
|
||||
//
|
||||
// voxel.geom
|
||||
// geometry shader
|
||||
//
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
//
|
||||
// VOXEL GEOMETRY SHADER
|
||||
//
|
||||
// Input: gl_VerticesIn/gl_PositionIn
|
||||
// GL_POINTS
|
||||
// Assumes vertex shader has not transformed coordinates
|
||||
// Each gl_PositionIn is the corner of voxel
|
||||
// varying voxelSize - which is the voxel size
|
||||
//
|
||||
// Note: In vertex shader doesn't do any transform. Therefore passing the 3D world coordinates xyz to us
|
||||
//
|
||||
// Output: GL_TRIANGLE_STRIP
|
||||
//
|
||||
// Issues:
|
||||
// how do we need to handle lighting of these colors??
|
||||
// how do we handle normals?
|
||||
// check for size=0 and don't output the primitive
|
||||
//
|
||||
|
||||
varying in float voxelSize[1];
|
||||
|
||||
const int VERTICES_PER_FACE = 4;
|
||||
const int COORD_PER_VERTEX = 3;
|
||||
const int COORD_PER_FACE = COORD_PER_VERTEX * VERTICES_PER_FACE;
|
||||
|
||||
void faceOfVoxel(vec4 corner, float scale, float[COORD_PER_FACE] facePoints, vec4 color, vec4 normal) {
|
||||
for (int v = 0; v < VERTICES_PER_FACE; v++ ) {
|
||||
vec4 vertex = corner;
|
||||
for (int c = 0; c < COORD_PER_VERTEX; c++ ) {
|
||||
int cIndex = c + (v * COORD_PER_VERTEX);
|
||||
vertex[c] += (facePoints[cIndex] * scale);
|
||||
}
|
||||
|
||||
gl_FrontColor = color * (gl_LightModel.ambient + gl_LightSource[0].ambient +
|
||||
gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
|
||||
|
||||
gl_Position = gl_ModelViewProjectionMatrix * vertex;
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
//increment variable
|
||||
int i;
|
||||
vec4 corner;
|
||||
float scale;
|
||||
|
||||
float bottomFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1 );
|
||||
float topFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1 );
|
||||
float rightFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1 );
|
||||
float leftFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1 );
|
||||
float frontFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 );
|
||||
float backFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1 );
|
||||
|
||||
vec4 bottomNormal = vec4(0.0, -1, 0.0, 0.0);
|
||||
vec4 topNormal = vec4(0.0, 1, 0.0, 0.0);
|
||||
vec4 rightNormal = vec4( -1, 0.0, 0.0, 0.0);
|
||||
vec4 leftNormal = vec4( 1, 0.0, 0.0, 0.0);
|
||||
vec4 frontNormal = vec4(0.0, 0.0, -1, 0.0);
|
||||
vec4 backNormal = vec4(0.0, 0.0, 1, 0.0);
|
||||
|
||||
for(i = 0; i < gl_VerticesIn; i++) {
|
||||
corner = gl_PositionIn[i];
|
||||
scale = voxelSize[i];
|
||||
faceOfVoxel(corner, scale, bottomFace, gl_FrontColorIn[i], bottomNormal);
|
||||
faceOfVoxel(corner, scale, topFace, gl_FrontColorIn[i], topNormal );
|
||||
faceOfVoxel(corner, scale, rightFace, gl_FrontColorIn[i], rightNormal );
|
||||
faceOfVoxel(corner, scale, leftFace, gl_FrontColorIn[i], leftNormal );
|
||||
faceOfVoxel(corner, scale, frontFace, gl_FrontColorIn[i], frontNormal );
|
||||
faceOfVoxel(corner, scale, backFace, gl_FrontColorIn[i], backNormal );
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@
|
|||
#include <QOpenGLFramebufferObject>
|
||||
#include <QObject>
|
||||
#include <QWheelEvent>
|
||||
#include <QScreen>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
#include <QTimer>
|
||||
|
@ -141,6 +142,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
QApplication(argc, argv),
|
||||
_window(new MainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
_toolWindow(NULL),
|
||||
_nodeThread(new QThread(this)),
|
||||
_datagramProcessor(),
|
||||
_undoStack(),
|
||||
|
@ -415,9 +417,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
|
||||
}
|
||||
|
||||
connect(_window, &MainWindow::windowGeometryChanged,
|
||||
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
|
||||
|
||||
_trayIcon->show();
|
||||
|
||||
// set the local loopback interface for local sounds from audio scripts
|
||||
|
@ -1917,8 +1916,6 @@ void Application::init() {
|
|||
_deferredLightingEffect.init();
|
||||
_glowEffect.init();
|
||||
_ambientOcclusionEffect.init();
|
||||
_voxelShader.init();
|
||||
_pointShader.init();
|
||||
|
||||
// TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager
|
||||
_avatarManager.init();
|
||||
|
@ -1989,8 +1986,6 @@ void Application::init() {
|
|||
|
||||
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
|
||||
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
|
||||
_voxels.setUseVoxelShader(false);
|
||||
_voxels.setVoxelsAsPoints(false);
|
||||
_voxels.setDisableFastVoxelPipeline(false);
|
||||
_voxels.init();
|
||||
|
||||
|
@ -2034,17 +2029,6 @@ void Application::init() {
|
|||
connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors()));
|
||||
|
||||
// set up our audio reflector
|
||||
_audioReflector.setMyAvatar(getAvatar());
|
||||
_audioReflector.setVoxels(_voxels.getTree());
|
||||
_audioReflector.setAudio(getAudio());
|
||||
_audioReflector.setAvatarManager(&_avatarManager);
|
||||
|
||||
connect(getAudio(), &Audio::processInboundAudio, &_audioReflector, &AudioReflector::processInboundAudio,Qt::DirectConnection);
|
||||
connect(getAudio(), &Audio::processLocalAudio, &_audioReflector, &AudioReflector::processLocalAudio,Qt::DirectConnection);
|
||||
connect(getAudio(), &Audio::preProcessOriginalInboundAudio, &_audioReflector,
|
||||
&AudioReflector::preProcessOriginalInboundAudio,Qt::DirectConnection);
|
||||
|
||||
connect(getAudio(), &Audio::muteToggled, AudioDeviceScriptingInterface::getInstance(),
|
||||
&AudioDeviceScriptingInterface::muteToggled, Qt::DirectConnection);
|
||||
|
||||
|
@ -2297,13 +2281,14 @@ void Application::updateCursor(float deltaTime) {
|
|||
PerformanceWarning warn(showWarnings, "Application::updateCursor()");
|
||||
|
||||
bool hideMouse = false;
|
||||
bool underMouse = _glWidget->underMouse();
|
||||
bool underMouse = QGuiApplication::topLevelAt(QCursor::pos()) ==
|
||||
Application::getInstance()->getWindow()->windowHandle();
|
||||
|
||||
static const int HIDE_CURSOR_TIMEOUT = 3 * USECS_PER_SECOND; // 3 second
|
||||
int elapsed = usecTimestampNow() - _lastMouseMove;
|
||||
if ((elapsed > HIDE_CURSOR_TIMEOUT && underMouse) ||
|
||||
if ((elapsed > HIDE_CURSOR_TIMEOUT) ||
|
||||
(OculusManager::isConnected() && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode))) {
|
||||
hideMouse = true;
|
||||
hideMouse = underMouse;
|
||||
}
|
||||
|
||||
setCursorVisible(!hideMouse);
|
||||
|
@ -2717,6 +2702,29 @@ bool Application::isHMDMode() const {
|
|||
}
|
||||
}
|
||||
|
||||
QRect Application::getDesirableApplicationGeometry() {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
||||
// If our parent window is on the HMD, then don't use it's geometry, instead use
|
||||
// the "main screen" geometry.
|
||||
HMDToolsDialog* hmdTools = Menu::getInstance()->getHMDToolsDialog();
|
||||
if (hmdTools && hmdTools->hasHMDScreen()) {
|
||||
QScreen* hmdScreen = hmdTools->getHMDScreen();
|
||||
QWindow* appWindow = getWindow()->windowHandle();
|
||||
QScreen* appScreen = appWindow->screen();
|
||||
|
||||
// if our app's screen is the hmd screen, we don't want to place the
|
||||
// running scripts widget on it. So we need to pick a better screen.
|
||||
// we will use the screen for the HMDTools since it's a guarenteed
|
||||
// better screen.
|
||||
if (appScreen == hmdScreen) {
|
||||
QScreen* betterScreen = hmdTools->windowHandle()->screen();
|
||||
applicationGeometry = betterScreen->geometry();
|
||||
}
|
||||
}
|
||||
return applicationGeometry;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// loadViewFrustum()
|
||||
//
|
||||
|
@ -3064,12 +3072,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
|
|||
glColor3f(1,0,0);
|
||||
_geometryCache.renderSphere(originSphereRadius, 15, 15);
|
||||
|
||||
// draw the audio reflector overlay
|
||||
{
|
||||
PerformanceTimer perfTimer("audio");
|
||||
_audioReflector.render();
|
||||
}
|
||||
|
||||
// Draw voxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
PerformanceTimer perfTimer("voxels");
|
||||
|
@ -3997,7 +3999,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
|
||||
scriptEngine->registerGlobalObject("SoundCache", &SoundCache::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels);
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
|
||||
#include "MainWindow.h"
|
||||
#include "Audio.h"
|
||||
#include "AudioReflector.h"
|
||||
#include "Camera.h"
|
||||
#include "DatagramProcessor.h"
|
||||
#include "Environment.h"
|
||||
|
@ -65,9 +64,7 @@
|
|||
#include "renderer/DeferredLightingEffect.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/GlowEffect.h"
|
||||
#include "renderer/PointShader.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
#include "renderer/VoxelShader.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
#include "ui/BandwidthMeter.h"
|
||||
|
@ -193,7 +190,6 @@ public:
|
|||
bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); }
|
||||
MyAvatar* getAvatar() { return _myAvatar; }
|
||||
Audio* getAudio() { return &_audio; }
|
||||
const AudioReflector* getAudioReflector() const { return &_audioReflector; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; }
|
||||
|
@ -297,8 +293,6 @@ public:
|
|||
|
||||
NodeBounds& getNodeBoundsDisplay() { return _nodeBoundsDisplay; }
|
||||
|
||||
VoxelShader& getVoxelShader() { return _voxelShader; }
|
||||
PointShader& getPointShader() { return _pointShader; }
|
||||
FileLogger* getLogger() { return _logger; }
|
||||
|
||||
glm::vec2 getViewportDimensions() const { return glm::vec2(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); }
|
||||
|
@ -327,6 +321,9 @@ public:
|
|||
// rendering of several elements depend on that
|
||||
// TODO: carry that information on the Camera as a setting
|
||||
bool isHMDMode() const;
|
||||
|
||||
QRect getDesirableApplicationGeometry();
|
||||
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -588,8 +585,6 @@ private:
|
|||
DeferredLightingEffect _deferredLightingEffect;
|
||||
GlowEffect _glowEffect;
|
||||
AmbientOcclusionEffect _ambientOcclusionEffect;
|
||||
VoxelShader _voxelShader;
|
||||
PointShader _pointShader;
|
||||
|
||||
Audio _audio;
|
||||
|
||||
|
@ -635,7 +630,6 @@ private:
|
|||
Overlays _overlays;
|
||||
ApplicationOverlay _applicationOverlay;
|
||||
|
||||
AudioReflector _audioReflector;
|
||||
RunningScriptsWidget* _runningScriptsWidget;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
bool _runningScriptsWidgetWasVisible;
|
||||
|
|
|
@ -103,10 +103,6 @@ Audio::Audio(QObject* parent) :
|
|||
_gverb(NULL),
|
||||
_iconColor(1.0f),
|
||||
_iconPulseTimeReference(usecTimestampNow()),
|
||||
_processSpatialAudio(false),
|
||||
_spatialAudioStart(0),
|
||||
_spatialAudioFinish(0),
|
||||
_spatialAudioRingBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, true), // random access mode
|
||||
_scopeEnabled(false),
|
||||
_scopeEnabledPause(false),
|
||||
_scopeInputOffset(0),
|
||||
|
@ -838,13 +834,6 @@ void Audio::handleAudioInput() {
|
|||
_lastInputLoudness = 0;
|
||||
}
|
||||
|
||||
// at this point we have clean monoAudioSamples, which match our target output...
|
||||
// this is what we should send to our interested listeners
|
||||
if (_processSpatialAudio && !_muted && !_isStereoInput && _audioOutput) {
|
||||
QByteArray monoInputData((char*)networkAudioSamples, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int16_t));
|
||||
emit processLocalAudio(_spatialAudioStart, monoInputData, _desiredInputFormat);
|
||||
}
|
||||
|
||||
if (!_isStereoInput && _proceduralAudioOutput) {
|
||||
processProceduralAudio(networkAudioSamples, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
}
|
||||
|
@ -984,35 +973,11 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou
|
|||
outputBuffer.resize(numDeviceOutputSamples * sizeof(int16_t));
|
||||
|
||||
const int16_t* receivedSamples;
|
||||
if (_processSpatialAudio) {
|
||||
unsigned int sampleTime = _spatialAudioStart;
|
||||
QByteArray buffer = inputBuffer;
|
||||
// copy the samples we'll resample from the ring buffer - this also
|
||||
// pushes the read pointer of the ring buffer forwards
|
||||
//receivedAudioStreamPopOutput.readSamples(receivedSamples, numNetworkOutputSamples);
|
||||
|
||||
// Accumulate direct transmission of audio from sender to receiver
|
||||
bool includeOriginal = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)
|
||||
if (includeOriginal) {
|
||||
emit preProcessOriginalInboundAudio(sampleTime, buffer, _desiredOutputFormat);
|
||||
addSpatialAudioToBuffer(sampleTime, buffer, numNetworkOutputSamples);
|
||||
}
|
||||
|
||||
// Send audio off for spatial processing
|
||||
emit processInboundAudio(sampleTime, buffer, _desiredOutputFormat);
|
||||
|
||||
// copy the samples we'll resample from the spatial audio ring buffer - this also
|
||||
// pushes the read pointer of the spatial audio ring buffer forwards
|
||||
_spatialAudioRingBuffer.readSamples(_outputProcessingBuffer, numNetworkOutputSamples);
|
||||
|
||||
// Advance the start point for the next packet of audio to arrive
|
||||
_spatialAudioStart += numNetworkOutputSamples / _desiredOutputFormat.channelCount();
|
||||
|
||||
receivedSamples = _outputProcessingBuffer;
|
||||
} else {
|
||||
// copy the samples we'll resample from the ring buffer - this also
|
||||
// pushes the read pointer of the ring buffer forwards
|
||||
//receivedAudioStreamPopOutput.readSamples(receivedSamples, numNetworkOutputSamples);
|
||||
|
||||
receivedSamples = reinterpret_cast<const int16_t*>(inputBuffer.data());
|
||||
}
|
||||
receivedSamples = reinterpret_cast<const int16_t*>(inputBuffer.data());
|
||||
|
||||
// copy the packet from the RB to the output
|
||||
linearResampling(receivedSamples,
|
||||
|
@ -1123,68 +1088,7 @@ void Audio::sendDownstreamAudioStatsPacket() {
|
|||
nodeList->writeDatagram(packet, dataAt - packet, audioMixer);
|
||||
}
|
||||
|
||||
// NOTE: numSamples is the total number of single channel samples, since callers will always call this with stereo
|
||||
// data we know that we will have 2x samples for each stereo time sample at the format's sample rate
|
||||
void Audio::addSpatialAudioToBuffer(unsigned int sampleTime, const QByteArray& spatialAudio, unsigned int numSamples) {
|
||||
// Calculate the number of remaining samples available. The source spatial audio buffer will get
|
||||
// clipped if there are insufficient samples available in the accumulation buffer.
|
||||
unsigned int remaining = _spatialAudioRingBuffer.getSampleCapacity() - _spatialAudioRingBuffer.samplesAvailable();
|
||||
|
||||
// Locate where in the accumulation buffer the new samples need to go
|
||||
if (sampleTime >= _spatialAudioFinish) {
|
||||
if (_spatialAudioStart == _spatialAudioFinish) {
|
||||
// Nothing in the spatial audio ring buffer yet, Just do a straight copy, clipping if necessary
|
||||
unsigned int sampleCount = (remaining < numSamples) ? remaining : numSamples;
|
||||
if (sampleCount) {
|
||||
_spatialAudioRingBuffer.writeSamples((int16_t*)spatialAudio.data(), sampleCount);
|
||||
}
|
||||
_spatialAudioFinish = _spatialAudioStart + sampleCount / _desiredOutputFormat.channelCount();
|
||||
} else {
|
||||
// Spatial audio ring buffer already has data, but there is no overlap with the new sample.
|
||||
// Compute the appropriate time delay and pad with silence until the new start time.
|
||||
unsigned int delay = sampleTime - _spatialAudioFinish;
|
||||
unsigned int delayCount = delay * _desiredOutputFormat.channelCount();
|
||||
unsigned int silentCount = (remaining < delayCount) ? remaining : delayCount;
|
||||
if (silentCount) {
|
||||
_spatialAudioRingBuffer.addSilentSamples(silentCount);
|
||||
}
|
||||
|
||||
// Recalculate the number of remaining samples
|
||||
remaining -= silentCount;
|
||||
unsigned int sampleCount = (remaining < numSamples) ? remaining : numSamples;
|
||||
|
||||
// Copy the new spatial audio to the accumulation ring buffer
|
||||
if (sampleCount) {
|
||||
_spatialAudioRingBuffer.writeSamples((int16_t*)spatialAudio.data(), sampleCount);
|
||||
}
|
||||
_spatialAudioFinish += (sampleCount + silentCount) / _desiredOutputFormat.channelCount();
|
||||
}
|
||||
} else {
|
||||
// There is overlap between the spatial audio buffer and the new sample, mix the overlap
|
||||
// Calculate the offset from the buffer's current read position, which should be located at _spatialAudioStart
|
||||
unsigned int offset = (sampleTime - _spatialAudioStart) * _desiredOutputFormat.channelCount();
|
||||
unsigned int mixedSamplesCount = (_spatialAudioFinish - sampleTime) * _desiredOutputFormat.channelCount();
|
||||
mixedSamplesCount = (mixedSamplesCount < numSamples) ? mixedSamplesCount : numSamples;
|
||||
|
||||
const int16_t* spatial = reinterpret_cast<const int16_t*>(spatialAudio.data());
|
||||
for (unsigned int i = 0; i < mixedSamplesCount; i++) {
|
||||
int existingSample = _spatialAudioRingBuffer[i + offset];
|
||||
int newSample = spatial[i];
|
||||
int sumOfSamples = existingSample + newSample;
|
||||
_spatialAudioRingBuffer[i + offset] = static_cast<int16_t>(glm::clamp<int>(sumOfSamples,
|
||||
std::numeric_limits<short>::min(), std::numeric_limits<short>::max()));
|
||||
}
|
||||
|
||||
// Copy the remaining unoverlapped spatial audio to the spatial audio buffer, if any
|
||||
unsigned int nonMixedSampleCount = numSamples - mixedSamplesCount;
|
||||
nonMixedSampleCount = (remaining < nonMixedSampleCount) ? remaining : nonMixedSampleCount;
|
||||
if (nonMixedSampleCount) {
|
||||
_spatialAudioRingBuffer.writeSamples((int16_t*)spatialAudio.data() + mixedSamplesCount, nonMixedSampleCount);
|
||||
// Extend the finish time by the amount of unoverlapped samples
|
||||
_spatialAudioFinish += nonMixedSampleCount / _desiredOutputFormat.channelCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Audio::mousePressEvent(int x, int y) {
|
||||
if (_iconBounds.contains(x, y)) {
|
||||
|
@ -1264,16 +1168,6 @@ void Audio::selectAudioSourceSine440() {
|
|||
_noiseSourceEnabled = !_toneSourceEnabled;
|
||||
}
|
||||
|
||||
void Audio::toggleAudioSpatialProcessing() {
|
||||
// spatial audio disabled for now
|
||||
_processSpatialAudio = false; //!_processSpatialAudio;
|
||||
if (_processSpatialAudio) {
|
||||
_spatialAudioStart = 0;
|
||||
_spatialAudioFinish = 0;
|
||||
_spatialAudioRingBuffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Take a pointer to the acquired microphone input samples and add procedural sounds
|
||||
void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
|
||||
float sample;
|
||||
|
@ -1995,11 +1889,6 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
|
|||
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
// setup spatial audio ringbuffer
|
||||
int numFrameSamples = _outputFormat.sampleRate() * _desiredOutputFormat.channelCount();
|
||||
_spatialAudioRingBuffer.resizeForFrameSize(numFrameSamples);
|
||||
_spatialAudioStart = _spatialAudioFinish = 0;
|
||||
|
||||
supportedFormat = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,8 +115,6 @@ public:
|
|||
int getNetworkSampleRate() { return SAMPLE_RATE; }
|
||||
int getNetworkBufferLengthSamplesPerChannel() { return NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; }
|
||||
|
||||
bool getProcessSpatialAudio() const { return _processSpatialAudio; }
|
||||
|
||||
float getInputRingBufferMsecsAvailable() const;
|
||||
float getInputRingBufferAverageMsecsAvailable() const { return (float)_inputRingBufferMsecsAvailableStats.getWindowAverage(); }
|
||||
|
||||
|
@ -131,7 +129,6 @@ public slots:
|
|||
void addReceivedAudioToStream(const QByteArray& audioByteArray);
|
||||
void parseAudioStreamStatsPacket(const QByteArray& packet);
|
||||
void parseAudioEnvironmentData(const QByteArray& packet);
|
||||
void addSpatialAudioToBuffer(unsigned int sampleTime, const QByteArray& spatialAudio, unsigned int numSamples);
|
||||
void handleAudioInput();
|
||||
void reset();
|
||||
void resetStats();
|
||||
|
@ -145,7 +142,6 @@ public slots:
|
|||
void toggleScopePause();
|
||||
void toggleStats();
|
||||
void toggleStatsShowInjectedStreams();
|
||||
void toggleAudioSpatialProcessing();
|
||||
void toggleStereoInput();
|
||||
void selectAudioScopeFiveFrames();
|
||||
void selectAudioScopeTwentyFrames();
|
||||
|
@ -254,11 +250,6 @@ private:
|
|||
float _iconColor;
|
||||
qint64 _iconPulseTimeReference;
|
||||
|
||||
bool _processSpatialAudio; /// Process received audio by spatial audio hooks
|
||||
unsigned int _spatialAudioStart; /// Start of spatial audio interval (in sample rate time base)
|
||||
unsigned int _spatialAudioFinish; /// End of spatial audio interval (in sample rate time base)
|
||||
AudioRingBuffer _spatialAudioRingBuffer; /// Spatially processed audio
|
||||
|
||||
// Process procedural audio by
|
||||
// 1. Echo to the local procedural output device
|
||||
// 2. Mix with the audio input
|
||||
|
|
|
@ -1,868 +0,0 @@
|
|||
//
|
||||
// AudioReflector.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/2/2014
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "AudioReflector.h"
|
||||
#include "Menu.h"
|
||||
|
||||
const float DEFAULT_PRE_DELAY = 20.0f; // this delay in msecs will always be added to all reflections
|
||||
const float DEFAULT_MS_DELAY_PER_METER = 3.0f;
|
||||
const float MINIMUM_ATTENUATION_TO_REFLECT = 1.0f / 256.0f;
|
||||
const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f;
|
||||
const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long
|
||||
const int DEFAULT_DIFFUSION_FANOUT = 5;
|
||||
const unsigned int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10;
|
||||
const float DEFAULT_LOCAL_ATTENUATION_FACTOR = 0.125;
|
||||
const float DEFAULT_COMB_FILTER_WINDOW = 0.05f; //ms delay differential to avoid
|
||||
|
||||
const float SLIGHTLY_SHORT = 0.999f; // slightly inside the distance so we're on the inside of the reflection point
|
||||
|
||||
const float DEFAULT_ABSORPTION_RATIO = 0.125; // 12.5% is absorbed
|
||||
const float DEFAULT_DIFFUSION_RATIO = 0.125; // 12.5% is diffused
|
||||
const float DEFAULT_ORIGINAL_ATTENUATION = 1.0f;
|
||||
const float DEFAULT_ECHO_ATTENUATION = 1.0f;
|
||||
|
||||
AudioReflector::AudioReflector(QObject* parent) :
|
||||
QObject(parent),
|
||||
_preDelay(DEFAULT_PRE_DELAY),
|
||||
_soundMsPerMeter(DEFAULT_MS_DELAY_PER_METER),
|
||||
_distanceAttenuationScalingFactor(DEFAULT_DISTANCE_SCALING_FACTOR),
|
||||
_localAudioAttenuationFactor(DEFAULT_LOCAL_ATTENUATION_FACTOR),
|
||||
_combFilterWindow(DEFAULT_COMB_FILTER_WINDOW),
|
||||
_diffusionFanout(DEFAULT_DIFFUSION_FANOUT),
|
||||
_absorptionRatio(DEFAULT_ABSORPTION_RATIO),
|
||||
_diffusionRatio(DEFAULT_DIFFUSION_RATIO),
|
||||
_originalSourceAttenuation(DEFAULT_ORIGINAL_ATTENUATION),
|
||||
_allEchoesAttenuation(DEFAULT_ECHO_ATTENUATION),
|
||||
_withDiffusion(false),
|
||||
_lastPreDelay(DEFAULT_PRE_DELAY),
|
||||
_lastSoundMsPerMeter(DEFAULT_MS_DELAY_PER_METER),
|
||||
_lastDistanceAttenuationScalingFactor(DEFAULT_DISTANCE_SCALING_FACTOR),
|
||||
_lastLocalAudioAttenuationFactor(DEFAULT_LOCAL_ATTENUATION_FACTOR),
|
||||
_lastDiffusionFanout(DEFAULT_DIFFUSION_FANOUT),
|
||||
_lastAbsorptionRatio(DEFAULT_ABSORPTION_RATIO),
|
||||
_lastDiffusionRatio(DEFAULT_DIFFUSION_RATIO),
|
||||
_lastDontDistanceAttenuate(false),
|
||||
_lastAlternateDistanceAttenuate(false)
|
||||
{
|
||||
_reflections = 0;
|
||||
_diffusionPathCount = 0;
|
||||
_officialAverageAttenuation = _averageAttenuation = 0.0f;
|
||||
_officialMaxAttenuation = _maxAttenuation = 0.0f;
|
||||
_officialMinAttenuation = _minAttenuation = 0.0f;
|
||||
_officialAverageDelay = _averageDelay = 0;
|
||||
_officialMaxDelay = _maxDelay = 0;
|
||||
_officialMinDelay = _minDelay = 0;
|
||||
_inboundEchoesCount = 0;
|
||||
_inboundEchoesSuppressedCount = 0;
|
||||
_localEchoesCount = 0;
|
||||
_localEchoesSuppressedCount = 0;
|
||||
}
|
||||
|
||||
bool AudioReflector::haveAttributesChanged() {
|
||||
|
||||
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
|
||||
bool withDiffusion = true;
|
||||
|
||||
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
|
||||
bool dontDistanceAttenuate = false;
|
||||
|
||||
//Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
|
||||
bool alternateDistanceAttenuate = false;
|
||||
|
||||
bool attributesChange = (_withDiffusion != withDiffusion
|
||||
|| _lastPreDelay != _preDelay
|
||||
|| _lastSoundMsPerMeter != _soundMsPerMeter
|
||||
|| _lastDistanceAttenuationScalingFactor != _distanceAttenuationScalingFactor
|
||||
|| _lastDiffusionFanout != _diffusionFanout
|
||||
|| _lastAbsorptionRatio != _absorptionRatio
|
||||
|| _lastDiffusionRatio != _diffusionRatio
|
||||
|| _lastDontDistanceAttenuate != dontDistanceAttenuate
|
||||
|| _lastAlternateDistanceAttenuate != alternateDistanceAttenuate);
|
||||
|
||||
if (attributesChange) {
|
||||
_withDiffusion = withDiffusion;
|
||||
_lastPreDelay = _preDelay;
|
||||
_lastSoundMsPerMeter = _soundMsPerMeter;
|
||||
_lastDistanceAttenuationScalingFactor = _distanceAttenuationScalingFactor;
|
||||
_lastDiffusionFanout = _diffusionFanout;
|
||||
_lastAbsorptionRatio = _absorptionRatio;
|
||||
_lastDiffusionRatio = _diffusionRatio;
|
||||
_lastDontDistanceAttenuate = dontDistanceAttenuate;
|
||||
_lastAlternateDistanceAttenuate = alternateDistanceAttenuate;
|
||||
}
|
||||
|
||||
return attributesChange;
|
||||
}
|
||||
|
||||
void AudioReflector::render() {
|
||||
|
||||
// if we're not set up yet, or we're not processing spatial audio, then exit early
|
||||
if (!_myAvatar || !_audio->getProcessSpatialAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use this oportunity to calculate our reflections
|
||||
calculateAllReflections();
|
||||
|
||||
// only render if we've been asked to do so
|
||||
bool renderPaths = false; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingRenderPaths)
|
||||
if (renderPaths) {
|
||||
drawRays();
|
||||
}
|
||||
}
|
||||
|
||||
// delay = 1ms per foot
|
||||
// = 3ms per meter
|
||||
float AudioReflector::getDelayFromDistance(float distance) {
|
||||
float delay = (_soundMsPerMeter * distance);
|
||||
bool includePreDelay = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
|
||||
if (includePreDelay) {
|
||||
delay += _preDelay;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
// attenuation = from the Audio Mixer
|
||||
float AudioReflector::getDistanceAttenuationCoefficient(float distance) {
|
||||
|
||||
|
||||
//!Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
|
||||
bool doDistanceAttenuation = true;
|
||||
|
||||
//!Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
|
||||
bool originalFormula = true;
|
||||
|
||||
float distanceCoefficient = 1.0f;
|
||||
|
||||
if (doDistanceAttenuation) {
|
||||
|
||||
if (originalFormula) {
|
||||
const float DISTANCE_SCALE = 2.5f;
|
||||
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
|
||||
const float DISTANCE_LOG_BASE = 2.5f;
|
||||
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
|
||||
|
||||
float distanceSquareToSource = distance * distance;
|
||||
|
||||
// calculate the distance coefficient using the distance to this node
|
||||
distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
|
||||
DISTANCE_SCALE_LOG +
|
||||
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient * getDistanceAttenuationScalingFactor());
|
||||
} else {
|
||||
|
||||
// From Fred: If we wanted something that would produce a tail that could go up to 5 seconds in a
|
||||
// really big room, that would suggest the sound still has to be in the audible after traveling about
|
||||
// 1500 meters. If it’s a sound of average volume, we probably have about 30 db, or 5 base2 orders
|
||||
// of magnitude we can drop down before the sound becomes inaudible. (That’s approximate headroom
|
||||
// based on a few sloppy assumptions.) So we could try a factor like 1 / (2^(D/300)) for starters.
|
||||
// 1 / (2^(D/300))
|
||||
const float DISTANCE_BASE = 2.0f;
|
||||
const float DISTANCE_DENOMINATOR = 300.0f;
|
||||
const float DISTANCE_NUMERATOR = 300.0f;
|
||||
distanceCoefficient = DISTANCE_NUMERATOR / powf(DISTANCE_BASE, (distance / DISTANCE_DENOMINATOR ));
|
||||
distanceCoefficient = std::min(1.0f, distanceCoefficient * getDistanceAttenuationScalingFactor());
|
||||
}
|
||||
}
|
||||
|
||||
return distanceCoefficient;
|
||||
}
|
||||
|
||||
glm::vec3 AudioReflector::getFaceNormal(BoxFace face) {
|
||||
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces);
|
||||
bool wantSlightRandomness = true;
|
||||
glm::vec3 faceNormal;
|
||||
const float MIN_RANDOM_LENGTH = 0.99f;
|
||||
const float MAX_RANDOM_LENGTH = 1.0f;
|
||||
const float NON_RANDOM_LENGTH = 1.0f;
|
||||
float normalLength = wantSlightRandomness ? randFloatInRange(MIN_RANDOM_LENGTH, MAX_RANDOM_LENGTH) : NON_RANDOM_LENGTH;
|
||||
float remainder = (1.0f - normalLength)/2.0f;
|
||||
float remainderSignA = randomSign();
|
||||
float remainderSignB = randomSign();
|
||||
|
||||
if (face == MIN_X_FACE) {
|
||||
faceNormal = glm::vec3(-normalLength, remainder * remainderSignA, remainder * remainderSignB);
|
||||
} else if (face == MAX_X_FACE) {
|
||||
faceNormal = glm::vec3(normalLength, remainder * remainderSignA, remainder * remainderSignB);
|
||||
} else if (face == MIN_Y_FACE) {
|
||||
faceNormal = glm::vec3(remainder * remainderSignA, -normalLength, remainder * remainderSignB);
|
||||
} else if (face == MAX_Y_FACE) {
|
||||
faceNormal = glm::vec3(remainder * remainderSignA, normalLength, remainder * remainderSignB);
|
||||
} else if (face == MIN_Z_FACE) {
|
||||
faceNormal = glm::vec3(remainder * remainderSignA, remainder * remainderSignB, -normalLength);
|
||||
} else if (face == MAX_Z_FACE) {
|
||||
faceNormal = glm::vec3(remainder * remainderSignA, remainder * remainderSignB, normalLength);
|
||||
}
|
||||
return faceNormal;
|
||||
}
|
||||
|
||||
// set up our buffers for our attenuated and delayed samples
|
||||
const int NUMBER_OF_CHANNELS = 2;
|
||||
|
||||
void AudioReflector::injectAudiblePoint(AudioSource source, const AudiblePoint& audiblePoint,
|
||||
const QByteArray& samples, unsigned int sampleTime, int sampleRate) {
|
||||
|
||||
bool wantEarSeparation = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars);
|
||||
bool wantStereo = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource);
|
||||
glm::vec3 rightEarPosition = wantEarSeparation ? _myAvatar->getHead()->getRightEarPosition() :
|
||||
_myAvatar->getHead()->getPosition();
|
||||
glm::vec3 leftEarPosition = wantEarSeparation ? _myAvatar->getHead()->getLeftEarPosition() :
|
||||
_myAvatar->getHead()->getPosition();
|
||||
|
||||
int totalNumberOfSamples = samples.size() / sizeof(int16_t);
|
||||
int totalNumberOfStereoSamples = samples.size() / (sizeof(int16_t) * NUMBER_OF_CHANNELS);
|
||||
|
||||
const int16_t* originalSamplesData = (const int16_t*)samples.constData();
|
||||
QByteArray attenuatedLeftSamples;
|
||||
QByteArray attenuatedRightSamples;
|
||||
attenuatedLeftSamples.resize(samples.size());
|
||||
attenuatedRightSamples.resize(samples.size());
|
||||
|
||||
int16_t* attenuatedLeftSamplesData = (int16_t*)attenuatedLeftSamples.data();
|
||||
int16_t* attenuatedRightSamplesData = (int16_t*)attenuatedRightSamples.data();
|
||||
|
||||
// calculate the distance to the ears
|
||||
float rightEarDistance = glm::distance(audiblePoint.location, rightEarPosition);
|
||||
float leftEarDistance = glm::distance(audiblePoint.location, leftEarPosition);
|
||||
|
||||
float rightEarDelayMsecs = getDelayFromDistance(rightEarDistance) + audiblePoint.delay;
|
||||
float leftEarDelayMsecs = getDelayFromDistance(leftEarDistance) + audiblePoint.delay;
|
||||
float averageEarDelayMsecs = (leftEarDelayMsecs + rightEarDelayMsecs) / 2.0f;
|
||||
|
||||
bool safeToInject = true; // assume the best
|
||||
|
||||
// check to see if this new injection point would be within the comb filter
|
||||
// suppression window for any of the existing known delays
|
||||
QMap<float, float>& knownDelays = (source == INBOUND_AUDIO) ? _inboundAudioDelays : _localAudioDelays;
|
||||
QMap<float, float>::const_iterator lowerBound = knownDelays.lowerBound(averageEarDelayMsecs - _combFilterWindow);
|
||||
if (lowerBound != knownDelays.end()) {
|
||||
float closestFound = lowerBound.value();
|
||||
float deltaToClosest = (averageEarDelayMsecs - closestFound);
|
||||
if (deltaToClosest > -_combFilterWindow && deltaToClosest < _combFilterWindow) {
|
||||
safeToInject = false;
|
||||
}
|
||||
}
|
||||
|
||||
// keep track of any of our suppressed echoes so we can report them in our statistics
|
||||
if (!safeToInject) {
|
||||
QVector<float>& suppressedEchoes = (source == INBOUND_AUDIO) ? _inboundEchoesSuppressed : _localEchoesSuppressed;
|
||||
suppressedEchoes << averageEarDelayMsecs;
|
||||
} else {
|
||||
knownDelays[averageEarDelayMsecs] = averageEarDelayMsecs;
|
||||
|
||||
_totalDelay += rightEarDelayMsecs + leftEarDelayMsecs;
|
||||
_delayCount += 2;
|
||||
_maxDelay = std::max(_maxDelay,rightEarDelayMsecs);
|
||||
_maxDelay = std::max(_maxDelay,leftEarDelayMsecs);
|
||||
_minDelay = std::min(_minDelay,rightEarDelayMsecs);
|
||||
_minDelay = std::min(_minDelay,leftEarDelayMsecs);
|
||||
|
||||
int rightEarDelay = rightEarDelayMsecs * sampleRate / MSECS_PER_SECOND;
|
||||
int leftEarDelay = leftEarDelayMsecs * sampleRate / MSECS_PER_SECOND;
|
||||
|
||||
float rightEarAttenuation = audiblePoint.attenuation *
|
||||
getDistanceAttenuationCoefficient(rightEarDistance + audiblePoint.distance);
|
||||
|
||||
float leftEarAttenuation = audiblePoint.attenuation *
|
||||
getDistanceAttenuationCoefficient(leftEarDistance + audiblePoint.distance);
|
||||
|
||||
_totalAttenuation += rightEarAttenuation + leftEarAttenuation;
|
||||
_attenuationCount += 2;
|
||||
_maxAttenuation = std::max(_maxAttenuation,rightEarAttenuation);
|
||||
_maxAttenuation = std::max(_maxAttenuation,leftEarAttenuation);
|
||||
_minAttenuation = std::min(_minAttenuation,rightEarAttenuation);
|
||||
_minAttenuation = std::min(_minAttenuation,leftEarAttenuation);
|
||||
|
||||
// run through the samples, and attenuate them
|
||||
for (int sample = 0; sample < totalNumberOfStereoSamples; sample++) {
|
||||
int16_t leftSample = originalSamplesData[sample * NUMBER_OF_CHANNELS];
|
||||
int16_t rightSample = leftSample;
|
||||
if (wantStereo) {
|
||||
rightSample = originalSamplesData[(sample * NUMBER_OF_CHANNELS) + 1];
|
||||
}
|
||||
|
||||
attenuatedLeftSamplesData[sample * NUMBER_OF_CHANNELS] =
|
||||
leftSample * leftEarAttenuation * _allEchoesAttenuation;
|
||||
attenuatedLeftSamplesData[sample * NUMBER_OF_CHANNELS + 1] = 0;
|
||||
|
||||
attenuatedRightSamplesData[sample * NUMBER_OF_CHANNELS] = 0;
|
||||
attenuatedRightSamplesData[sample * NUMBER_OF_CHANNELS + 1] =
|
||||
rightSample * rightEarAttenuation * _allEchoesAttenuation;
|
||||
}
|
||||
|
||||
// now inject the attenuated array with the appropriate delay
|
||||
unsigned int sampleTimeLeft = sampleTime + leftEarDelay;
|
||||
unsigned int sampleTimeRight = sampleTime + rightEarDelay;
|
||||
|
||||
_audio->addSpatialAudioToBuffer(sampleTimeLeft, attenuatedLeftSamples, totalNumberOfSamples);
|
||||
_audio->addSpatialAudioToBuffer(sampleTimeRight, attenuatedRightSamples, totalNumberOfSamples);
|
||||
|
||||
_injectedEchoes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioReflector::preProcessOriginalInboundAudio(unsigned int sampleTime,
|
||||
QByteArray& samples, const QAudioFormat& format) {
|
||||
|
||||
if (_originalSourceAttenuation != 1.0f) {
|
||||
int numberOfSamples = (samples.size() / sizeof(int16_t));
|
||||
int16_t* sampleData = (int16_t*)samples.data();
|
||||
for (int i = 0; i < numberOfSamples; i++) {
|
||||
sampleData[i] = sampleData[i] * _originalSourceAttenuation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AudioReflector::processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
|
||||
bool processLocalAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
|
||||
if (processLocalAudio) {
|
||||
const int NUM_CHANNELS_INPUT = 1;
|
||||
const int NUM_CHANNELS_OUTPUT = 2;
|
||||
const int EXPECTED_SAMPLE_RATE = 24000;
|
||||
if (format.channelCount() == NUM_CHANNELS_INPUT && format.sampleRate() == EXPECTED_SAMPLE_RATE) {
|
||||
QAudioFormat outputFormat = format;
|
||||
outputFormat.setChannelCount(NUM_CHANNELS_OUTPUT);
|
||||
QByteArray stereoInputData(samples.size() * NUM_CHANNELS_OUTPUT, 0);
|
||||
int numberOfSamples = (samples.size() / sizeof(int16_t));
|
||||
int16_t* monoSamples = (int16_t*)samples.data();
|
||||
int16_t* stereoSamples = (int16_t*)stereoInputData.data();
|
||||
|
||||
for (int i = 0; i < numberOfSamples; i++) {
|
||||
stereoSamples[i* NUM_CHANNELS_OUTPUT] = monoSamples[i] * _localAudioAttenuationFactor;
|
||||
stereoSamples[(i * NUM_CHANNELS_OUTPUT) + 1] = monoSamples[i] * _localAudioAttenuationFactor;
|
||||
}
|
||||
_localAudioDelays.clear();
|
||||
_localEchoesSuppressed.clear();
|
||||
echoAudio(LOCAL_AUDIO, sampleTime, stereoInputData, outputFormat);
|
||||
_localEchoesCount = _localAudioDelays.size();
|
||||
_localEchoesSuppressedCount = _localEchoesSuppressed.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
|
||||
_inboundAudioDelays.clear();
|
||||
_inboundEchoesSuppressed.clear();
|
||||
echoAudio(INBOUND_AUDIO, sampleTime, samples, format);
|
||||
_inboundEchoesCount = _inboundAudioDelays.size();
|
||||
_inboundEchoesSuppressedCount = _inboundEchoesSuppressed.size();
|
||||
}
|
||||
|
||||
void AudioReflector::echoAudio(AudioSource source, unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
_maxDelay = 0;
|
||||
_maxAttenuation = 0.0f;
|
||||
_minDelay = std::numeric_limits<int>::max();
|
||||
_minAttenuation = std::numeric_limits<float>::max();
|
||||
_totalDelay = 0.0f;
|
||||
_delayCount = 0;
|
||||
_totalAttenuation = 0.0f;
|
||||
_attenuationCount = 0;
|
||||
|
||||
// depending on if we're processing local or external audio, pick the correct points vector
|
||||
QVector<AudiblePoint>& audiblePoints = source == INBOUND_AUDIO ? _inboundAudiblePoints : _localAudiblePoints;
|
||||
|
||||
int injectCalls = 0;
|
||||
_injectedEchoes = 0;
|
||||
foreach(const AudiblePoint& audiblePoint, audiblePoints) {
|
||||
injectCalls++;
|
||||
injectAudiblePoint(source, audiblePoint, samples, sampleTime, format.sampleRate());
|
||||
}
|
||||
|
||||
/*
|
||||
qDebug() << "injectCalls=" << injectCalls;
|
||||
qDebug() << "_injectedEchoes=" << _injectedEchoes;
|
||||
*/
|
||||
|
||||
_averageDelay = _delayCount == 0 ? 0 : _totalDelay / _delayCount;
|
||||
_averageAttenuation = _attenuationCount == 0 ? 0 : _totalAttenuation / _attenuationCount;
|
||||
|
||||
if (_reflections == 0) {
|
||||
_minDelay = 0.0f;
|
||||
_minAttenuation = 0.0f;
|
||||
}
|
||||
|
||||
_officialMaxDelay = _maxDelay;
|
||||
_officialMinDelay = _minDelay;
|
||||
_officialMaxAttenuation = _maxAttenuation;
|
||||
_officialMinAttenuation = _minAttenuation;
|
||||
_officialAverageDelay = _averageDelay;
|
||||
_officialAverageAttenuation = _averageAttenuation;
|
||||
|
||||
}
|
||||
|
||||
void AudioReflector::drawVector(const glm::vec3& start, const glm::vec3& end, const glm::vec3& color) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(2.0);
|
||||
|
||||
// Draw the vector itself
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(color.x,color.y,color.z);
|
||||
glVertex3f(start.x, start.y, start.z);
|
||||
glVertex3f(end.x, end.y, end.z);
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioPath::AudioPath(AudioSource source, const glm::vec3& origin, const glm::vec3& direction,
|
||||
float attenuation, float delay, float distance,bool isDiffusion, int bounceCount) :
|
||||
|
||||
source(source),
|
||||
isDiffusion(isDiffusion),
|
||||
startPoint(origin),
|
||||
startDirection(direction),
|
||||
startDelay(delay),
|
||||
startAttenuation(attenuation),
|
||||
|
||||
lastPoint(origin),
|
||||
lastDirection(direction),
|
||||
lastDistance(distance),
|
||||
lastDelay(delay),
|
||||
lastAttenuation(attenuation),
|
||||
bounceCount(bounceCount),
|
||||
|
||||
finalized(false),
|
||||
reflections()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioReflector::addAudioPath(AudioSource source, const glm::vec3& origin, const glm::vec3& initialDirection,
|
||||
float initialAttenuation, float initialDelay, float initialDistance, bool isDiffusion) {
|
||||
|
||||
AudioPath* path = new AudioPath(source, origin, initialDirection, initialAttenuation, initialDelay,
|
||||
initialDistance, isDiffusion, 0);
|
||||
|
||||
QVector<AudioPath*>& audioPaths = source == INBOUND_AUDIO ? _inboundAudioPaths : _localAudioPaths;
|
||||
|
||||
audioPaths.push_back(path);
|
||||
}
|
||||
|
||||
// NOTE: This is a prototype of an eventual utility that will identify the speaking sources for the inbound audio
|
||||
// stream. It's not currently called but will be added soon.
|
||||
void AudioReflector::identifyAudioSources() {
|
||||
// looking for audio sources....
|
||||
foreach (const AvatarSharedPointer& avatarPointer, _avatarManager->getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (!avatar->isInitialized()) {
|
||||
continue;
|
||||
}
|
||||
qDebug() << "avatar["<< avatar <<"] loudness:" << avatar->getAudioLoudness();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::calculateAllReflections() {
|
||||
// only recalculate when we've moved, or if the attributes have changed
|
||||
// TODO: what about case where new voxels are added in front of us???
|
||||
bool wantHeadOrientation = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingHeadOriented);
|
||||
glm::quat orientation = wantHeadOrientation ? _myAvatar->getHead()->getFinalOrientationInWorldFrame() : _myAvatar->getOrientation();
|
||||
glm::vec3 origin = _myAvatar->getHead()->getPosition();
|
||||
glm::vec3 listenerPosition = _myAvatar->getHead()->getPosition();
|
||||
|
||||
bool shouldRecalc = _reflections == 0
|
||||
|| !isSimilarPosition(origin, _origin)
|
||||
|| !isSimilarOrientation(orientation, _orientation)
|
||||
|| !isSimilarPosition(listenerPosition, _listenerPosition)
|
||||
|| haveAttributesChanged();
|
||||
|
||||
if (shouldRecalc) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
quint64 start = usecTimestampNow();
|
||||
_origin = origin;
|
||||
_orientation = orientation;
|
||||
_listenerPosition = listenerPosition;
|
||||
analyzePaths(); // actually does the work
|
||||
quint64 end = usecTimestampNow();
|
||||
const bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
qDebug() << "newCalculateAllReflections() elapsed=" << (end - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::drawRays() {
|
||||
const glm::vec3 RED(1,0,0);
|
||||
const glm::vec3 GREEN(0,1,0);
|
||||
const glm::vec3 BLUE(0,0,1);
|
||||
const glm::vec3 CYAN(0,1,1);
|
||||
|
||||
int diffusionNumber = 0;
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
// draw the paths for inbound audio
|
||||
foreach(AudioPath* const& path, _inboundAudioPaths) {
|
||||
// if this is an original reflection, draw it in RED
|
||||
if (path->isDiffusion) {
|
||||
diffusionNumber++;
|
||||
drawPath(path, GREEN);
|
||||
} else {
|
||||
drawPath(path, RED);
|
||||
}
|
||||
}
|
||||
|
||||
bool processLocalAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
|
||||
if (processLocalAudio) {
|
||||
// draw the paths for local audio
|
||||
foreach(AudioPath* const& path, _localAudioPaths) {
|
||||
// if this is an original reflection, draw it in RED
|
||||
if (path->isDiffusion) {
|
||||
diffusionNumber++;
|
||||
drawPath(path, CYAN);
|
||||
} else {
|
||||
drawPath(path, BLUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::drawPath(AudioPath* path, const glm::vec3& originalColor) {
|
||||
glm::vec3 start = path->startPoint;
|
||||
glm::vec3 color = originalColor;
|
||||
const float COLOR_ADJUST_PER_BOUNCE = 0.75f;
|
||||
|
||||
foreach (glm::vec3 end, path->reflections) {
|
||||
drawVector(start, end, color);
|
||||
start = end;
|
||||
color = color * COLOR_ADJUST_PER_BOUNCE;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::clearPaths() {
|
||||
// clear our inbound audio paths
|
||||
foreach(AudioPath* const& path, _inboundAudioPaths) {
|
||||
delete path;
|
||||
}
|
||||
_inboundAudioPaths.clear();
|
||||
_inboundAudiblePoints.clear(); // clear our inbound audible points
|
||||
|
||||
// clear our local audio paths
|
||||
foreach(AudioPath* const& path, _localAudioPaths) {
|
||||
delete path;
|
||||
}
|
||||
_localAudioPaths.clear();
|
||||
_localAudiblePoints.clear(); // clear our local audible points
|
||||
}
|
||||
|
||||
// Here's how this works: we have an array of AudioPaths, we loop on all of our currently calculating audio
|
||||
// paths, and calculate one ray per path. If that ray doesn't reflect, or reaches a max distance/attenuation, then it
|
||||
// is considered finalized.
|
||||
// If the ray hits a surface, then, based on the characteristics of that surface, it will calculate the new
|
||||
// attenuation, path length, and delay for the primary path. For surfaces that have diffusion, it will also create
|
||||
// fanout number of new paths, those new paths will have an origin of the reflection point, and an initial attenuation
|
||||
// of their diffusion ratio. Those new paths will be added to the active audio paths, and be analyzed for the next loop.
|
||||
void AudioReflector::analyzePaths() {
|
||||
clearPaths();
|
||||
|
||||
// add our initial paths
|
||||
glm::vec3 right = glm::normalize(_orientation * IDENTITY_RIGHT);
|
||||
glm::vec3 up = glm::normalize(_orientation * IDENTITY_UP);
|
||||
glm::vec3 front = glm::normalize(_orientation * IDENTITY_FRONT);
|
||||
glm::vec3 left = -right;
|
||||
glm::vec3 down = -up;
|
||||
glm::vec3 back = -front;
|
||||
glm::vec3 frontRightUp = glm::normalize(front + right + up);
|
||||
glm::vec3 frontLeftUp = glm::normalize(front + left + up);
|
||||
glm::vec3 backRightUp = glm::normalize(back + right + up);
|
||||
glm::vec3 backLeftUp = glm::normalize(back + left + up);
|
||||
glm::vec3 frontRightDown = glm::normalize(front + right + down);
|
||||
glm::vec3 frontLeftDown = glm::normalize(front + left + down);
|
||||
glm::vec3 backRightDown = glm::normalize(back + right + down);
|
||||
glm::vec3 backLeftDown = glm::normalize(back + left + down);
|
||||
|
||||
float initialAttenuation = 1.0f;
|
||||
|
||||
bool wantPreDelay = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
|
||||
float preDelay = wantPreDelay ? _preDelay : 0.0f;
|
||||
|
||||
// NOTE: we're still calculating our initial paths based on the listeners position. But the analysis code has been
|
||||
// updated to support individual sound sources (which is how we support diffusion), we can use this new paradigm to
|
||||
// add support for individual sound sources, and more directional sound sources
|
||||
|
||||
addAudioPath(INBOUND_AUDIO, _origin, front, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, right, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, up, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, down, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, back, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, left, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, frontRightUp, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, frontLeftUp, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, backRightUp, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, backLeftUp, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, frontRightDown, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, frontLeftDown, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, backRightDown, initialAttenuation, preDelay);
|
||||
addAudioPath(INBOUND_AUDIO, _origin, backLeftDown, initialAttenuation, preDelay);
|
||||
|
||||
// the original paths for the local audio are directional to the front of the origin
|
||||
addAudioPath(LOCAL_AUDIO, _origin, front, initialAttenuation, preDelay);
|
||||
addAudioPath(LOCAL_AUDIO, _origin, frontRightUp, initialAttenuation, preDelay);
|
||||
addAudioPath(LOCAL_AUDIO, _origin, frontLeftUp, initialAttenuation, preDelay);
|
||||
addAudioPath(LOCAL_AUDIO, _origin, frontRightDown, initialAttenuation, preDelay);
|
||||
addAudioPath(LOCAL_AUDIO, _origin, frontLeftDown, initialAttenuation, preDelay);
|
||||
|
||||
// loop through all our audio paths and keep analyzing them until they complete
|
||||
int steps = 0;
|
||||
int acitvePaths = _inboundAudioPaths.size() + _localAudioPaths.size(); // when we start, all paths are active
|
||||
while(acitvePaths > 0) {
|
||||
acitvePaths = analyzePathsSingleStep();
|
||||
steps++;
|
||||
}
|
||||
_reflections = _inboundAudiblePoints.size() + _localAudiblePoints.size();
|
||||
_diffusionPathCount = countDiffusionPaths();
|
||||
}
|
||||
|
||||
int AudioReflector::countDiffusionPaths() {
|
||||
int diffusionCount = 0;
|
||||
|
||||
foreach(AudioPath* const& path, _inboundAudioPaths) {
|
||||
if (path->isDiffusion) {
|
||||
diffusionCount++;
|
||||
}
|
||||
}
|
||||
foreach(AudioPath* const& path, _localAudioPaths) {
|
||||
if (path->isDiffusion) {
|
||||
diffusionCount++;
|
||||
}
|
||||
}
|
||||
return diffusionCount;
|
||||
}
|
||||
|
||||
int AudioReflector::analyzePathsSingleStep() {
|
||||
// iterate all the active sound paths, calculate one step per active path
|
||||
int activePaths = 0;
|
||||
|
||||
QVector<AudioPath*>* pathsLists[] = { &_inboundAudioPaths, &_localAudioPaths };
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(pathsLists) / sizeof(pathsLists[0]); i++) {
|
||||
|
||||
QVector<AudioPath*>& pathList = *pathsLists[i];
|
||||
|
||||
foreach(AudioPath* const& path, pathList) {
|
||||
|
||||
glm::vec3 start = path->lastPoint;
|
||||
glm::vec3 direction = path->lastDirection;
|
||||
OctreeElement* elementHit; // output from findRayIntersection
|
||||
float distance; // output from findRayIntersection
|
||||
BoxFace face; // output from findRayIntersection
|
||||
|
||||
if (!path->finalized) {
|
||||
activePaths++;
|
||||
|
||||
if (path->bounceCount > ABSOLUTE_MAXIMUM_BOUNCE_COUNT) {
|
||||
path->finalized = true;
|
||||
} else if (_voxels->findRayIntersection(start, direction, elementHit, distance, face)) {
|
||||
// TODO: we need to decide how we want to handle locking on the ray intersection, if we force lock,
|
||||
// we get an accurate picture, but it could prevent rendering of the voxels. If we trylock (default),
|
||||
// we might not get ray intersections where they may exist, but we can't really detect that case...
|
||||
// add last parameter of Octree::Lock to force locking
|
||||
handlePathPoint(path, distance, elementHit, face);
|
||||
|
||||
} else {
|
||||
// If we didn't intersect, but this was a diffusion ray, then we will go ahead and cast a short ray out
|
||||
// from our last known point, in the last known direction, and leave that sound source hanging there
|
||||
if (path->isDiffusion) {
|
||||
const float MINIMUM_RANDOM_DISTANCE = 0.25f;
|
||||
const float MAXIMUM_RANDOM_DISTANCE = 0.5f;
|
||||
float distance = randFloatInRange(MINIMUM_RANDOM_DISTANCE, MAXIMUM_RANDOM_DISTANCE);
|
||||
handlePathPoint(path, distance, NULL, UNKNOWN_FACE);
|
||||
} else {
|
||||
path->finalized = true; // if it doesn't intersect, then it is finished
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return activePaths;
|
||||
}
|
||||
|
||||
void AudioReflector::handlePathPoint(AudioPath* path, float distance, OctreeElement* elementHit, BoxFace face) {
|
||||
glm::vec3 start = path->lastPoint;
|
||||
glm::vec3 direction = path->lastDirection;
|
||||
glm::vec3 end = start + (direction * (distance * SLIGHTLY_SHORT));
|
||||
|
||||
float currentReflectiveAttenuation = path->lastAttenuation; // only the reflective components
|
||||
float currentDelay = path->lastDelay; // start with our delay so far
|
||||
float pathDistance = path->lastDistance;
|
||||
|
||||
pathDistance += glm::distance(start, end);
|
||||
|
||||
float toListenerDistance = glm::distance(end, _listenerPosition);
|
||||
|
||||
// adjust our current delay by just the delay from the most recent ray
|
||||
currentDelay += getDelayFromDistance(distance);
|
||||
|
||||
// now we know the current attenuation for the "perfect" reflection case, but we now incorporate
|
||||
// our surface materials to determine how much of this ray is absorbed, reflected, and diffused
|
||||
SurfaceCharacteristics material = getSurfaceCharacteristics(elementHit);
|
||||
|
||||
float reflectiveAttenuation = currentReflectiveAttenuation * material.reflectiveRatio;
|
||||
float totalDiffusionAttenuation = currentReflectiveAttenuation * material.diffusionRatio;
|
||||
|
||||
bool wantDiffusions = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
|
||||
int fanout = wantDiffusions ? _diffusionFanout : 0;
|
||||
|
||||
float partialDiffusionAttenuation = fanout < 1 ? 0.0f : totalDiffusionAttenuation / (float)fanout;
|
||||
|
||||
// total delay includes the bounce back to listener
|
||||
float totalDelay = currentDelay + getDelayFromDistance(toListenerDistance);
|
||||
float toListenerAttenuation = getDistanceAttenuationCoefficient(toListenerDistance + pathDistance);
|
||||
|
||||
// if our resulting partial diffusion attenuation, is still above our minimum attenuation
|
||||
// then we add new paths for each diffusion point
|
||||
if ((partialDiffusionAttenuation * toListenerAttenuation) > MINIMUM_ATTENUATION_TO_REFLECT
|
||||
&& totalDelay < MAXIMUM_DELAY_MS) {
|
||||
|
||||
// diffusions fan out from random places on the semisphere of the collision point
|
||||
for(int i = 0; i < fanout; i++) {
|
||||
glm::vec3 diffusion;
|
||||
|
||||
// We're creating a random normal here. But we want it to be relatively dramatic compared to how we handle
|
||||
// our slightly random surface normals.
|
||||
const float MINIMUM_RANDOM_LENGTH = 0.5f;
|
||||
const float MAXIMUM_RANDOM_LENGTH = 1.0f;
|
||||
float randomness = randFloatInRange(MINIMUM_RANDOM_LENGTH, MAXIMUM_RANDOM_LENGTH);
|
||||
float remainder = (1.0f - randomness)/2.0f;
|
||||
float remainderSignA = randomSign();
|
||||
float remainderSignB = randomSign();
|
||||
|
||||
if (face == MIN_X_FACE) {
|
||||
diffusion = glm::vec3(-randomness, remainder * remainderSignA, remainder * remainderSignB);
|
||||
} else if (face == MAX_X_FACE) {
|
||||
diffusion = glm::vec3(randomness, remainder * remainderSignA, remainder * remainderSignB);
|
||||
} else if (face == MIN_Y_FACE) {
|
||||
diffusion = glm::vec3(remainder * remainderSignA, -randomness, remainder * remainderSignB);
|
||||
} else if (face == MAX_Y_FACE) {
|
||||
diffusion = glm::vec3(remainder * remainderSignA, randomness, remainder * remainderSignB);
|
||||
} else if (face == MIN_Z_FACE) {
|
||||
diffusion = glm::vec3(remainder * remainderSignA, remainder * remainderSignB, -randomness);
|
||||
} else if (face == MAX_Z_FACE) {
|
||||
diffusion = glm::vec3(remainder * remainderSignA, remainder * remainderSignB, randomness);
|
||||
} else if (face == UNKNOWN_FACE) {
|
||||
float randomnessX = randFloatInRange(MINIMUM_RANDOM_LENGTH, MAXIMUM_RANDOM_LENGTH);
|
||||
float randomnessY = randFloatInRange(MINIMUM_RANDOM_LENGTH, MAXIMUM_RANDOM_LENGTH);
|
||||
float randomnessZ = randFloatInRange(MINIMUM_RANDOM_LENGTH, MAXIMUM_RANDOM_LENGTH);
|
||||
diffusion = glm::vec3(direction.x * randomnessX, direction.y * randomnessY, direction.z * randomnessZ);
|
||||
}
|
||||
|
||||
diffusion = glm::normalize(diffusion);
|
||||
|
||||
// add new audio path for these diffusions, the new path's source is the same as the original source
|
||||
addAudioPath(path->source, end, diffusion, partialDiffusionAttenuation, currentDelay, pathDistance, true);
|
||||
}
|
||||
} else {
|
||||
const bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
if ((partialDiffusionAttenuation * toListenerAttenuation) <= MINIMUM_ATTENUATION_TO_REFLECT) {
|
||||
qDebug() << "too quiet to diffuse";
|
||||
qDebug() << " partialDiffusionAttenuation=" << partialDiffusionAttenuation;
|
||||
qDebug() << " toListenerAttenuation=" << toListenerAttenuation;
|
||||
qDebug() << " result=" << (partialDiffusionAttenuation * toListenerAttenuation);
|
||||
qDebug() << " MINIMUM_ATTENUATION_TO_REFLECT=" << MINIMUM_ATTENUATION_TO_REFLECT;
|
||||
}
|
||||
if (totalDelay > MAXIMUM_DELAY_MS) {
|
||||
qDebug() << "too delayed to diffuse";
|
||||
qDebug() << " totalDelay=" << totalDelay;
|
||||
qDebug() << " MAXIMUM_DELAY_MS=" << MAXIMUM_DELAY_MS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if our reflective attenuation is above our minimum, then add our reflection point and
|
||||
// allow our path to continue
|
||||
if (((reflectiveAttenuation + totalDiffusionAttenuation) * toListenerAttenuation) > MINIMUM_ATTENUATION_TO_REFLECT
|
||||
&& totalDelay < MAXIMUM_DELAY_MS) {
|
||||
|
||||
// add this location, as the reflective attenuation as well as the total diffusion attenuation
|
||||
// NOTE: we add the delay to the audible point, not back to the listener. The additional delay
|
||||
// and attenuation to the listener is recalculated at the point where we actually inject the
|
||||
// audio so that it can be adjusted to ear position
|
||||
AudiblePoint point = {end, currentDelay, (reflectiveAttenuation + totalDiffusionAttenuation), pathDistance};
|
||||
|
||||
QVector<AudiblePoint>& audiblePoints = path->source == INBOUND_AUDIO ? _inboundAudiblePoints : _localAudiblePoints;
|
||||
|
||||
audiblePoints.push_back(point);
|
||||
|
||||
// add this location to the path points, so we can visualize it
|
||||
path->reflections.push_back(end);
|
||||
|
||||
// now, if our reflective attenuation is over our minimum then keep going...
|
||||
if (reflectiveAttenuation * toListenerAttenuation > MINIMUM_ATTENUATION_TO_REFLECT) {
|
||||
glm::vec3 faceNormal = getFaceNormal(face);
|
||||
path->lastDirection = glm::normalize(glm::reflect(direction,faceNormal));
|
||||
path->lastPoint = end;
|
||||
path->lastAttenuation = reflectiveAttenuation;
|
||||
path->lastDelay = currentDelay;
|
||||
path->lastDistance = pathDistance;
|
||||
path->bounceCount++;
|
||||
} else {
|
||||
path->finalized = true; // if we're too quiet, then we're done
|
||||
}
|
||||
} else {
|
||||
const bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
if (((reflectiveAttenuation + totalDiffusionAttenuation) * toListenerAttenuation) <= MINIMUM_ATTENUATION_TO_REFLECT) {
|
||||
qDebug() << "too quiet to add audible point";
|
||||
qDebug() << " reflectiveAttenuation + totalDiffusionAttenuation=" << (reflectiveAttenuation + totalDiffusionAttenuation);
|
||||
qDebug() << " toListenerAttenuation=" << toListenerAttenuation;
|
||||
qDebug() << " result=" << ((reflectiveAttenuation + totalDiffusionAttenuation) * toListenerAttenuation);
|
||||
qDebug() << " MINIMUM_ATTENUATION_TO_REFLECT=" << MINIMUM_ATTENUATION_TO_REFLECT;
|
||||
}
|
||||
if (totalDelay > MAXIMUM_DELAY_MS) {
|
||||
qDebug() << "too delayed to add audible point";
|
||||
qDebug() << " totalDelay=" << totalDelay;
|
||||
qDebug() << " MAXIMUM_DELAY_MS=" << MAXIMUM_DELAY_MS;
|
||||
}
|
||||
}
|
||||
path->finalized = true; // if we're too quiet, then we're done
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: eventually we will add support for different surface characteristics based on the element
|
||||
// that is hit, which is why we pass in the elementHit to this helper function. But for now, all
|
||||
// surfaces have the same characteristics
|
||||
SurfaceCharacteristics AudioReflector::getSurfaceCharacteristics(OctreeElement* elementHit) {
|
||||
SurfaceCharacteristics result = { getReflectiveRatio(), _absorptionRatio, _diffusionRatio };
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioReflector::setReflectiveRatio(float ratio) {
|
||||
float safeRatio = std::max(0.0f, std::min(ratio, 1.0f));
|
||||
float currentReflectiveRatio = (1.0f - (_absorptionRatio + _diffusionRatio));
|
||||
float halfDifference = (safeRatio - currentReflectiveRatio) / 2.0f;
|
||||
|
||||
// evenly distribute the difference between the two other ratios
|
||||
_absorptionRatio -= halfDifference;
|
||||
_diffusionRatio -= halfDifference;
|
||||
}
|
||||
|
||||
void AudioReflector::setAbsorptionRatio(float ratio) {
|
||||
float safeRatio = std::max(0.0f, std::min(ratio, 1.0f));
|
||||
_absorptionRatio = safeRatio;
|
||||
const float MAX_COMBINED_RATIO = 1.0f;
|
||||
if (_absorptionRatio + _diffusionRatio > MAX_COMBINED_RATIO) {
|
||||
_diffusionRatio = MAX_COMBINED_RATIO - _absorptionRatio;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioReflector::setDiffusionRatio(float ratio) {
|
||||
float safeRatio = std::max(0.0f, std::min(ratio, 1.0f));
|
||||
_diffusionRatio = safeRatio;
|
||||
const float MAX_COMBINED_RATIO = 1.0f;
|
||||
if (_absorptionRatio + _diffusionRatio > MAX_COMBINED_RATIO) {
|
||||
_absorptionRatio = MAX_COMBINED_RATIO - _diffusionRatio;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,254 +0,0 @@
|
|||
//
|
||||
// AudioReflector.h
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/2/2014
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef interface_AudioReflector_h
|
||||
#define interface_AudioReflector_h
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "Audio.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
enum AudioSource {
|
||||
LOCAL_AUDIO,
|
||||
INBOUND_AUDIO
|
||||
};
|
||||
|
||||
class AudioPath {
|
||||
public:
|
||||
AudioPath(AudioSource source = INBOUND_AUDIO, const glm::vec3& origin = glm::vec3(0.0f),
|
||||
const glm::vec3& direction = glm::vec3(0.0f), float attenuation = 1.0f,
|
||||
float delay = 0.0f, float distance = 0.0f, bool isDiffusion = false, int bounceCount = 0);
|
||||
|
||||
AudioSource source;
|
||||
bool isDiffusion;
|
||||
glm::vec3 startPoint;
|
||||
glm::vec3 startDirection;
|
||||
float startDelay;
|
||||
float startAttenuation;
|
||||
|
||||
glm::vec3 lastPoint;
|
||||
glm::vec3 lastDirection;
|
||||
float lastDistance;
|
||||
float lastDelay;
|
||||
float lastAttenuation;
|
||||
unsigned int bounceCount;
|
||||
|
||||
bool finalized;
|
||||
QVector<glm::vec3> reflections;
|
||||
};
|
||||
|
||||
class AudiblePoint {
|
||||
public:
|
||||
glm::vec3 location; /// location of the audible point
|
||||
float delay; /// includes total delay including pre delay to the point of the audible location, not to the listener's ears
|
||||
float attenuation; /// only the reflective & diffusive portion of attenuation, doesn't include distance attenuation
|
||||
float distance; /// includes total distance to the point of the audible location, not to the listener's ears
|
||||
};
|
||||
|
||||
class SurfaceCharacteristics {
|
||||
public:
|
||||
float reflectiveRatio;
|
||||
float absorptionRatio;
|
||||
float diffusionRatio;
|
||||
};
|
||||
|
||||
class AudioReflector : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioReflector(QObject* parent = NULL);
|
||||
|
||||
// setup functions to configure the resources used by the AudioReflector
|
||||
void setVoxels(VoxelTree* voxels) { _voxels = voxels; }
|
||||
void setMyAvatar(MyAvatar* myAvatar) { _myAvatar = myAvatar; }
|
||||
void setAudio(Audio* audio) { _audio = audio; }
|
||||
void setAvatarManager(AvatarManager* avatarManager) { _avatarManager = avatarManager; }
|
||||
|
||||
void render(); /// must be called in the application render loop
|
||||
|
||||
void preProcessOriginalInboundAudio(unsigned int sampleTime, QByteArray& samples, const QAudioFormat& format);
|
||||
void processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
|
||||
void processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
|
||||
|
||||
public slots:
|
||||
// statistics
|
||||
int getReflections() const { return _reflections; }
|
||||
float getAverageDelayMsecs() const { return _officialAverageDelay; }
|
||||
float getAverageAttenuation() const { return _officialAverageAttenuation; }
|
||||
float getMaxDelayMsecs() const { return _officialMaxDelay; }
|
||||
float getMaxAttenuation() const { return _officialMaxAttenuation; }
|
||||
float getMinDelayMsecs() const { return _officialMinDelay; }
|
||||
float getMinAttenuation() const { return _officialMinAttenuation; }
|
||||
float getDelayFromDistance(float distance);
|
||||
int getDiffusionPathCount() const { return _diffusionPathCount; }
|
||||
int getEchoesInjected() const { return _inboundEchoesCount + _localEchoesCount; }
|
||||
int getEchoesSuppressed() const { return _inboundEchoesSuppressedCount + _localEchoesSuppressedCount; }
|
||||
|
||||
/// ms of delay added to all echos
|
||||
float getPreDelay() const { return _preDelay; }
|
||||
void setPreDelay(float preDelay) { _preDelay = preDelay; }
|
||||
|
||||
/// ms per meter that sound travels, larger means slower, which sounds bigger
|
||||
float getSoundMsPerMeter() const { return _soundMsPerMeter; }
|
||||
void setSoundMsPerMeter(float soundMsPerMeter) { _soundMsPerMeter = soundMsPerMeter; }
|
||||
|
||||
/// scales attenuation to be louder or softer than the default distance attenuation
|
||||
float getDistanceAttenuationScalingFactor() const { return _distanceAttenuationScalingFactor; }
|
||||
void setDistanceAttenuationScalingFactor(float factor) { _distanceAttenuationScalingFactor = factor; }
|
||||
|
||||
/// scales attenuation of local audio to be louder or softer than the default attenuation
|
||||
float getLocalAudioAttenuationFactor() const { return _localAudioAttenuationFactor; }
|
||||
void setLocalAudioAttenuationFactor(float factor) { _localAudioAttenuationFactor = factor; }
|
||||
|
||||
/// ms window in which we will suppress echoes to reduce comb filter effects
|
||||
float getCombFilterWindow() const { return _combFilterWindow; }
|
||||
void setCombFilterWindow(float value) { _combFilterWindow = value; }
|
||||
|
||||
/// number of points of diffusion from each reflection point, as fanout increases there are more chances for secondary
|
||||
/// echoes, but each diffusion ray is quieter and therefore more likely to be below the sound floor
|
||||
int getDiffusionFanout() const { return _diffusionFanout; }
|
||||
void setDiffusionFanout(int fanout) { _diffusionFanout = fanout; }
|
||||
|
||||
/// ratio 0.0 - 1.0 of amount of each ray that is absorbed upon hitting a surface
|
||||
float getAbsorptionRatio() const { return _absorptionRatio; }
|
||||
void setAbsorptionRatio(float ratio);
|
||||
|
||||
// ratio 0.0 - 1.0 of amount of each ray that is diffused upon hitting a surface
|
||||
float getDiffusionRatio() const { return _diffusionRatio; }
|
||||
void setDiffusionRatio(float ratio);
|
||||
|
||||
// remaining ratio 0.0 - 1.0 of amount of each ray that is cleanly reflected upon hitting a surface
|
||||
float getReflectiveRatio() const { return (1.0f - (_absorptionRatio + _diffusionRatio)); }
|
||||
void setReflectiveRatio(float ratio);
|
||||
|
||||
// wet/dry mix - these don't affect any reflection calculations, only the final mix volumes
|
||||
float getOriginalSourceAttenuation() const { return _originalSourceAttenuation; }
|
||||
void setOriginalSourceAttenuation(float value) { _originalSourceAttenuation = value; }
|
||||
float getEchoesAttenuation() const { return _allEchoesAttenuation; }
|
||||
void setEchoesAttenuation(float value) { _allEchoesAttenuation = value; }
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
VoxelTree* _voxels; // used to access voxel scene
|
||||
MyAvatar* _myAvatar; // access to listener
|
||||
Audio* _audio; // access to audio API
|
||||
AvatarManager* _avatarManager; // access to avatar manager API
|
||||
|
||||
// Helpers for drawing
|
||||
void drawVector(const glm::vec3& start, const glm::vec3& end, const glm::vec3& color);
|
||||
|
||||
// helper for generically calculating attenuation based on distance
|
||||
float getDistanceAttenuationCoefficient(float distance);
|
||||
|
||||
// statistics
|
||||
int _reflections;
|
||||
int _diffusionPathCount;
|
||||
int _delayCount;
|
||||
float _totalDelay;
|
||||
float _averageDelay;
|
||||
float _maxDelay;
|
||||
float _minDelay;
|
||||
float _officialAverageDelay;
|
||||
float _officialMaxDelay;
|
||||
float _officialMinDelay;
|
||||
int _attenuationCount;
|
||||
float _totalAttenuation;
|
||||
float _averageAttenuation;
|
||||
float _maxAttenuation;
|
||||
float _minAttenuation;
|
||||
float _officialAverageAttenuation;
|
||||
float _officialMaxAttenuation;
|
||||
float _officialMinAttenuation;
|
||||
|
||||
|
||||
glm::vec3 _listenerPosition;
|
||||
glm::vec3 _origin;
|
||||
glm::quat _orientation;
|
||||
|
||||
QVector<AudioPath*> _inboundAudioPaths; /// audio paths we're processing for inbound audio
|
||||
QVector<AudiblePoint> _inboundAudiblePoints; /// the audible points that have been calculated from the inbound audio paths
|
||||
QMap<float, float> _inboundAudioDelays; /// delay times for currently injected audio points
|
||||
QVector<float> _inboundEchoesSuppressed; /// delay times for currently injected audio points
|
||||
int _inboundEchoesCount;
|
||||
int _inboundEchoesSuppressedCount;
|
||||
|
||||
QVector<AudioPath*> _localAudioPaths; /// audio paths we're processing for local audio
|
||||
QVector<AudiblePoint> _localAudiblePoints; /// the audible points that have been calculated from the local audio paths
|
||||
QMap<float, float> _localAudioDelays; /// delay times for currently injected audio points
|
||||
QVector<float> _localEchoesSuppressed; /// delay times for currently injected audio points
|
||||
int _localEchoesCount;
|
||||
int _localEchoesSuppressedCount;
|
||||
|
||||
// adds a sound source to begin an audio path trace, these can be the initial sound sources with their directional properties,
|
||||
// as well as diffusion sound sources
|
||||
void addAudioPath(AudioSource source, const glm::vec3& origin, const glm::vec3& initialDirection, float initialAttenuation,
|
||||
float initialDelay, float initialDistance = 0.0f, bool isDiffusion = false);
|
||||
|
||||
// helper that handles audioPath analysis
|
||||
int analyzePathsSingleStep();
|
||||
void handlePathPoint(AudioPath* path, float distance, OctreeElement* elementHit, BoxFace face);
|
||||
void clearPaths();
|
||||
void analyzePaths();
|
||||
void drawRays();
|
||||
void drawPath(AudioPath* path, const glm::vec3& originalColor);
|
||||
void calculateAllReflections();
|
||||
int countDiffusionPaths();
|
||||
glm::vec3 getFaceNormal(BoxFace face);
|
||||
void identifyAudioSources();
|
||||
|
||||
void injectAudiblePoint(AudioSource source, const AudiblePoint& audiblePoint, const QByteArray& samples, unsigned int sampleTime, int sampleRate);
|
||||
void echoAudio(AudioSource source, unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format);
|
||||
|
||||
// return the surface characteristics of the element we hit
|
||||
SurfaceCharacteristics getSurfaceCharacteristics(OctreeElement* elementHit = NULL);
|
||||
|
||||
|
||||
QMutex _mutex;
|
||||
|
||||
float _preDelay;
|
||||
float _soundMsPerMeter;
|
||||
float _distanceAttenuationScalingFactor;
|
||||
float _localAudioAttenuationFactor;
|
||||
float _combFilterWindow;
|
||||
int _diffusionFanout; // number of points of diffusion from each reflection point
|
||||
|
||||
// all elements have the same material for now...
|
||||
float _absorptionRatio;
|
||||
float _diffusionRatio;
|
||||
float _reflectiveRatio;
|
||||
|
||||
// wet/dry mix - these don't affect any reflection calculations, only the final mix volumes
|
||||
float _originalSourceAttenuation; /// each sample of original signal will be multiplied by this
|
||||
float _allEchoesAttenuation; /// each sample of all echo signals will be multiplied by this
|
||||
|
||||
// remember the last known values at calculation
|
||||
bool haveAttributesChanged();
|
||||
|
||||
bool _withDiffusion;
|
||||
float _lastPreDelay;
|
||||
float _lastSoundMsPerMeter;
|
||||
float _lastDistanceAttenuationScalingFactor;
|
||||
float _lastLocalAudioAttenuationFactor;
|
||||
int _lastDiffusionFanout;
|
||||
float _lastAbsorptionRatio;
|
||||
float _lastDiffusionRatio;
|
||||
bool _lastDontDistanceAttenuate;
|
||||
bool _lastAlternateDistanceAttenuate;
|
||||
|
||||
int _injectedEchoes;
|
||||
};
|
||||
|
||||
|
||||
#endif // interface_AudioReflector_h
|
|
@ -1303,6 +1303,10 @@ void Menu::bandwidthDetails() {
|
|||
connect(_bandwidthDialog, SIGNAL(closed()), SLOT(bandwidthDetailsClosed()));
|
||||
|
||||
_bandwidthDialog->show();
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_bandwidthDialog->windowHandle());
|
||||
}
|
||||
}
|
||||
_bandwidthDialog->raise();
|
||||
}
|
||||
|
@ -1384,6 +1388,9 @@ void Menu::toggleConsole() {
|
|||
void Menu::toggleToolWindow() {
|
||||
QMainWindow* toolWindow = Application::getInstance()->getToolWindow();
|
||||
toolWindow->setVisible(!toolWindow->isVisible());
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(toolWindow->windowHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::audioMuteToggled() {
|
||||
|
@ -1406,6 +1413,9 @@ void Menu::octreeStatsDetails() {
|
|||
Application::getInstance()->getOcteeSceneStats());
|
||||
connect(_octreeStatsDialog, SIGNAL(closed()), SLOT(octreeStatsDetailsClosed()));
|
||||
_octreeStatsDialog->show();
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_octreeStatsDialog->windowHandle());
|
||||
}
|
||||
}
|
||||
_octreeStatsDialog->raise();
|
||||
}
|
||||
|
@ -1586,6 +1596,9 @@ void Menu::lodTools() {
|
|||
_lodToolsDialog = new LodToolsDialog(Application::getInstance()->getGLWidget());
|
||||
connect(_lodToolsDialog, SIGNAL(closed()), SLOT(lodToolsClosed()));
|
||||
_lodToolsDialog->show();
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_lodToolsDialog->windowHandle());
|
||||
}
|
||||
}
|
||||
_lodToolsDialog->raise();
|
||||
}
|
||||
|
|
|
@ -123,7 +123,6 @@ public:
|
|||
LodToolsDialog* getLodToolsDialog() const { return _lodToolsDialog; }
|
||||
HMDToolsDialog* getHMDToolsDialog() const { return _hmdToolsDialog; }
|
||||
int getMaxVoxels() const { return _maxVoxels; }
|
||||
QAction* getUseVoxelShader() const { return _useVoxelShader; }
|
||||
|
||||
bool getShadowsEnabled() const;
|
||||
|
||||
|
@ -304,7 +303,6 @@ private:
|
|||
float _avatarLODIncreaseFPS;
|
||||
float _avatarLODDistanceMultiplier;
|
||||
int _boundaryLevelAdjust;
|
||||
QAction* _useVoxelShader;
|
||||
int _maxVoxelPacketsPerSecond;
|
||||
QString replaceLastOccurrence(QChar search, QChar replace, QString string);
|
||||
quint64 _lastAdjust;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <MetavoxelClientManager.h>
|
||||
|
||||
#include "renderer/ProgramObject.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
|
||||
class HeightfieldBaseLayerBatch;
|
||||
class HeightfieldSplatBatch;
|
||||
|
|
|
@ -63,7 +63,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
|
|||
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
|
||||
|
||||
// Same font properties as textWidth()
|
||||
// Same font properties as textSize()
|
||||
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
|
||||
float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO;
|
||||
|
||||
|
|
|
@ -841,14 +841,14 @@ void GeometryReader::run() {
|
|||
|
||||
if (urlValid) {
|
||||
// Let's read the binaries from the network
|
||||
QByteArray fileBinary = _reply->readAll();
|
||||
if (fileBinary.isEmpty() || fileBinary.isNull()) {
|
||||
throw QString("Read File binary is empty?!");
|
||||
}
|
||||
|
||||
FBXGeometry fbxgeo;
|
||||
if (_url.path().toLower().endsWith(".svo")) {
|
||||
QByteArray fileBinary = _reply->readAll();
|
||||
if (fileBinary.isEmpty() || fileBinary.isNull()) {
|
||||
throw QString("Read File binary is empty?!");
|
||||
}
|
||||
fbxgeo = readSVO(fileBinary);
|
||||
|
||||
} else if (_url.path().toLower().endsWith(".fbx")) {
|
||||
bool grabLightmaps = true;
|
||||
float lightmapLevel = 1.0f;
|
||||
|
@ -860,7 +860,7 @@ void GeometryReader::run() {
|
|||
} else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) {
|
||||
lightmapLevel = 3.5f;
|
||||
}
|
||||
fbxgeo = readFBX(fileBinary, _mapping, grabLightmaps, lightmapLevel);
|
||||
fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
|
||||
}
|
||||
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
|
||||
} else {
|
||||
|
|
|
@ -134,38 +134,21 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
_snappedToRegistrationPoint = false;
|
||||
}
|
||||
|
||||
void Model::initProgram(ProgramObject& program, Model::Locations& locations, int specularTextureUnit) {
|
||||
void Model::initProgram(ProgramObject& program, Model::Locations& locations, bool link) {
|
||||
if (link) {
|
||||
program.bindAttributeLocation("tangent", gpu::Stream::TANGENT);
|
||||
program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1);
|
||||
program.link();
|
||||
}
|
||||
program.bind();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
||||
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
||||
|
||||
glBindAttribLocation(program.programId(), 4, "tangent");
|
||||
|
||||
glLinkProgram(program.programId());
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
glBindAttribLocation(program.programId(), gpu::Stream::TANGENT, "tangent");
|
||||
|
||||
glBindAttribLocation(program.programId(), gpu::Stream::TEXCOORD1, "texcoord1");
|
||||
|
||||
glLinkProgram(program.programId());
|
||||
|
||||
locations.tangent = program.attributeLocation("tangent");
|
||||
|
||||
locations.alphaThreshold = program.uniformLocation("alphaThreshold");
|
||||
|
||||
locations.texcoordMatrices = program.uniformLocation("texcoordMatrices");
|
||||
|
||||
locations.emissiveParams = program.uniformLocation("emissiveParams");
|
||||
|
||||
|
||||
program.setUniformValue("diffuseMap", 0);
|
||||
|
||||
program.setUniformValue("normalMap", 1);
|
||||
|
||||
int loc = program.uniformLocation("specularMap");
|
||||
|
@ -184,50 +167,22 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, int
|
|||
locations.emissiveTextureUnit = -1;
|
||||
}
|
||||
|
||||
if (!program.isLinked()) {
|
||||
program.release();
|
||||
}
|
||||
|
||||
program.release();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) {
|
||||
|
||||
initProgram(program, locations, specularTextureUnit);
|
||||
|
||||
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
||||
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
||||
|
||||
glBindAttribLocation(program.programId(), 5, "clusterIndices");
|
||||
|
||||
glBindAttribLocation(program.programId(), 6, "clusterWeights");
|
||||
|
||||
glLinkProgram(program.programId());
|
||||
|
||||
#endif
|
||||
|
||||
// HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite
|
||||
|
||||
glBindAttribLocation(program.programId(), gpu::Stream::SKIN_CLUSTER_INDEX, "clusterIndices");
|
||||
|
||||
glBindAttribLocation(program.programId(), gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights");
|
||||
|
||||
glLinkProgram(program.programId());
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) {
|
||||
program.bindAttributeLocation("tangent", gpu::Stream::TANGENT);
|
||||
program.bindAttributeLocation("texcoord1", gpu::Stream::TEXCOORD1);
|
||||
program.bindAttributeLocation("clusterIndices", gpu::Stream::SKIN_CLUSTER_INDEX);
|
||||
program.bindAttributeLocation("clusterWeights", gpu::Stream::SKIN_CLUSTER_WEIGHT);
|
||||
program.link();
|
||||
|
||||
initProgram(program, locations, false);
|
||||
|
||||
program.bind();
|
||||
|
||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
||||
|
||||
|
||||
|
||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
||||
locations.clusterIndices = program.attributeLocation("clusterIndices");
|
||||
|
||||
locations.clusterWeights = program.attributeLocation("clusterWeights");
|
||||
|
||||
program.release();
|
||||
|
@ -269,7 +224,6 @@ void Model::init() {
|
|||
if (!_program.isLinked()) {
|
||||
_program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert");
|
||||
_program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
|
||||
_program.link();
|
||||
|
||||
initProgram(_program, _locations);
|
||||
|
||||
|
@ -277,116 +231,101 @@ void Model::init() {
|
|||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.frag");
|
||||
_normalMapProgram.link();
|
||||
|
||||
|
||||
initProgram(_normalMapProgram, _normalMapLocations);
|
||||
|
||||
_specularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model.vert");
|
||||
_specularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_specular_map.frag");
|
||||
_specularMapProgram.link();
|
||||
|
||||
|
||||
initProgram(_specularMapProgram, _specularMapLocations);
|
||||
|
||||
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
|
||||
_normalSpecularMapProgram.link();
|
||||
|
||||
initProgram(_normalSpecularMapProgram, _normalSpecularMapLocations, 2);
|
||||
|
||||
initProgram(_normalSpecularMapProgram, _normalSpecularMapLocations);
|
||||
|
||||
_translucentProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model.vert");
|
||||
_translucentProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_translucent.frag");
|
||||
_translucentProgram.link();
|
||||
|
||||
|
||||
initProgram(_translucentProgram, _translucentLocations);
|
||||
|
||||
// Lightmap
|
||||
_lightmapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_lightmap.vert");
|
||||
_lightmapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model_lightmap.frag");
|
||||
_lightmapProgram.link();
|
||||
|
||||
|
||||
initProgram(_lightmapProgram, _lightmapLocations);
|
||||
|
||||
_lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert");
|
||||
_lightmapNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_lightmap_normal_map.frag");
|
||||
_lightmapNormalMapProgram.link();
|
||||
|
||||
|
||||
initProgram(_lightmapNormalMapProgram, _lightmapNormalMapLocations);
|
||||
|
||||
_lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_lightmap.vert");
|
||||
_lightmapSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_lightmap_specular_map.frag");
|
||||
_lightmapSpecularMapProgram.link();
|
||||
|
||||
|
||||
initProgram(_lightmapSpecularMapProgram, _lightmapSpecularMapLocations);
|
||||
|
||||
_lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_lightmap_normal_map.vert");
|
||||
_lightmapNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_lightmap_normal_specular_map.frag");
|
||||
_lightmapNormalSpecularMapProgram.link();
|
||||
|
||||
initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations, 2);
|
||||
|
||||
initProgram(_lightmapNormalSpecularMapProgram, _lightmapNormalSpecularMapLocations);
|
||||
// end lightmap
|
||||
|
||||
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow.frag");
|
||||
_shadowProgram.link();
|
||||
|
||||
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag");
|
||||
_skinProgram.link();
|
||||
|
||||
|
||||
initSkinProgram(_skinProgram, _skinLocations);
|
||||
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.frag");
|
||||
_skinNormalMapProgram.link();
|
||||
|
||||
|
||||
initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations);
|
||||
|
||||
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_specular_map.frag");
|
||||
_skinSpecularMapProgram.link();
|
||||
|
||||
|
||||
initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations);
|
||||
|
||||
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_normal_specular_map.frag");
|
||||
_skinNormalSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2);
|
||||
|
||||
initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations);
|
||||
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow.frag");
|
||||
_skinShadowProgram.link();
|
||||
|
||||
|
||||
initSkinProgram(_skinShadowProgram, _skinShadowLocations);
|
||||
|
||||
_skinTranslucentProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinTranslucentProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_translucent.frag");
|
||||
_skinTranslucentProgram.link();
|
||||
|
||||
|
||||
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations);
|
||||
}
|
||||
}
|
||||
|
@ -600,8 +539,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) {
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
float bestTriangleDistance = std::numeric_limits<float>::max();
|
||||
bool someTriangleHit = false;
|
||||
|
||||
float distanceToSubMesh;
|
||||
BoxFace subMeshFace;
|
||||
|
@ -615,7 +552,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) {
|
||||
if (distanceToSubMesh < bestDistance) {
|
||||
if (pickAgainstTriangles) {
|
||||
someTriangleHit = false;
|
||||
if (!_calculatedMeshTrianglesValid) {
|
||||
recalculateMeshBoxes(pickAgainstTriangles);
|
||||
}
|
||||
|
@ -628,9 +564,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
float thisTriangleDistance;
|
||||
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
||||
if (thisTriangleDistance < bestDistance) {
|
||||
bestTriangleDistance = thisTriangleDistance;
|
||||
someTriangleHit = true;
|
||||
|
||||
bestDistance = thisTriangleDistance;
|
||||
intersectedSomething = true;
|
||||
face = subMeshFace;
|
||||
|
|
|
@ -347,7 +347,7 @@ private:
|
|||
static Locations _lightmapSpecularMapLocations;
|
||||
static Locations _lightmapNormalSpecularMapLocations;
|
||||
|
||||
static void initProgram(ProgramObject& program, Locations& locations, int specularTextureUnit = 1);
|
||||
static void initProgram(ProgramObject& program, Locations& locations, bool link = true);
|
||||
|
||||
class SkinLocations : public Locations {
|
||||
public:
|
||||
|
@ -363,7 +363,7 @@ private:
|
|||
static SkinLocations _skinShadowLocations;
|
||||
static SkinLocations _skinTranslucentLocations;
|
||||
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1);
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations);
|
||||
|
||||
QVector<AABox> _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes
|
||||
bool _calculatedMeshBoxesValid;
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
//
|
||||
// PointShader.cpp
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 10/30/13.
|
||||
// Copyright 2013 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 this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include "Application.h"
|
||||
#include "PointShader.h"
|
||||
#include "ProgramObject.h"
|
||||
#include "RenderUtil.h"
|
||||
|
||||
PointShader::PointShader()
|
||||
: _initialized(false)
|
||||
{
|
||||
_program = NULL;
|
||||
}
|
||||
|
||||
PointShader::~PointShader() {
|
||||
if (_initialized) {
|
||||
delete _program;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramObject* PointShader::createPointShaderProgram(const QString& name) {
|
||||
ProgramObject* program = new ProgramObject();
|
||||
program->addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/" + name + ".vert" );
|
||||
program->link();
|
||||
return program;
|
||||
}
|
||||
|
||||
void PointShader::init() {
|
||||
if (_initialized) {
|
||||
qDebug("[ERROR] PointShader is already initialized.");
|
||||
return;
|
||||
}
|
||||
_program = createPointShaderProgram("point_size");
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void PointShader::begin() {
|
||||
_program->bind();
|
||||
}
|
||||
|
||||
void PointShader::end() {
|
||||
_program->release();
|
||||
}
|
||||
|
||||
int PointShader::attributeLocation(const char* name) const {
|
||||
if (_program) {
|
||||
return _program->attributeLocation(name);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int PointShader::uniformLocation(const char* name) const {
|
||||
if (_program) {
|
||||
return _program->uniformLocation(name);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void PointShader::setUniformValue(int uniformLocation, float value) {
|
||||
_program->setUniformValue(uniformLocation, value);
|
||||
}
|
||||
|
||||
void PointShader::setUniformValue(int uniformLocation, const glm::vec3& value) {
|
||||
_program->setUniformValue(uniformLocation, value.x, value.y, value.z);
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// PointShader.h
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 10/30/13.
|
||||
// Copyright 2013 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_PointShader_h
|
||||
#define hifi_PointShader_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class ProgramObject;
|
||||
|
||||
/// A shader program that draws voxels as points with variable sizes
|
||||
class PointShader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PointShader();
|
||||
~PointShader();
|
||||
|
||||
void init();
|
||||
|
||||
/// Starts using the voxel point shader program.
|
||||
void begin();
|
||||
|
||||
/// Stops using the voxel point shader program.
|
||||
void end();
|
||||
|
||||
/// Gets access to attributes from the shader program
|
||||
int attributeLocation(const char* name) const;
|
||||
int uniformLocation(const char* name) const;
|
||||
void setUniformValue(int uniformLocation, float value);
|
||||
void setUniformValue(int uniformLocation, const glm::vec3& value);
|
||||
|
||||
static ProgramObject* createPointShaderProgram(const QString& name);
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
ProgramObject* _program;
|
||||
};
|
||||
|
||||
#endif // hifi_PointShader_h
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// VoxelShader.cpp
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 9/22/13.
|
||||
// Copyright 2013 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 this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include "Application.h"
|
||||
#include "VoxelShader.h"
|
||||
#include "ProgramObject.h"
|
||||
#include "RenderUtil.h"
|
||||
|
||||
VoxelShader::VoxelShader()
|
||||
: _initialized(false)
|
||||
{
|
||||
_program = NULL;
|
||||
}
|
||||
|
||||
VoxelShader::~VoxelShader() {
|
||||
if (_initialized) {
|
||||
delete _program;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramObject* VoxelShader::createGeometryShaderProgram(const QString& name) {
|
||||
ProgramObject* program = new ProgramObject();
|
||||
program->addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/passthrough.vert" );
|
||||
program->addShaderFromSourceFile(QGLShader::Geometry, Application::resourcesPath() + "shaders/" + name + ".geom" );
|
||||
program->setGeometryInputType(GL_POINTS);
|
||||
program->setGeometryOutputType(GL_TRIANGLE_STRIP);
|
||||
const int VERTICES_PER_FACE = 4;
|
||||
const int FACES_PER_VOXEL = 6;
|
||||
const int VERTICES_PER_VOXEL = VERTICES_PER_FACE * FACES_PER_VOXEL;
|
||||
program->setGeometryOutputVertexCount(VERTICES_PER_VOXEL);
|
||||
program->link();
|
||||
return program;
|
||||
}
|
||||
|
||||
void VoxelShader::init() {
|
||||
if (_initialized) {
|
||||
qDebug("[ERROR] TestProgram is already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
_program = createGeometryShaderProgram("voxel");
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void VoxelShader::begin() {
|
||||
_program->bind();
|
||||
}
|
||||
|
||||
void VoxelShader::end() {
|
||||
_program->release();
|
||||
}
|
||||
|
||||
int VoxelShader::attributeLocation(const char * name) const {
|
||||
if (_program) {
|
||||
return _program->attributeLocation(name);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
//
|
||||
// VoxelShader.h
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 9/23/13.
|
||||
// Copyright 2013 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_VoxelShader_h
|
||||
#define hifi_VoxelShader_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class ProgramObject;
|
||||
|
||||
/// A generic full screen glow effect.
|
||||
class VoxelShader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VoxelShader();
|
||||
~VoxelShader();
|
||||
|
||||
void init();
|
||||
|
||||
/// Starts using the voxel geometry shader effect.
|
||||
void begin();
|
||||
|
||||
/// Stops using the voxel geometry shader effect.
|
||||
void end();
|
||||
|
||||
/// Gets access to attributes from the shader program
|
||||
int attributeLocation(const char * name) const;
|
||||
|
||||
static ProgramObject* createGeometryShaderProgram(const QString& name);
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
|
||||
bool _initialized;
|
||||
|
||||
ProgramObject* _program;
|
||||
};
|
||||
|
||||
#endif // hifi_VoxelShader_h
|
|
@ -124,7 +124,7 @@ void ChatWindow::showEvent(QShowEvent* event) {
|
|||
if (!event->spontaneous()) {
|
||||
_ui->messagePlainTextEdit->setFocus();
|
||||
}
|
||||
const QRect parentGeometry = parentWidget()->geometry();
|
||||
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
|
||||
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
|
||||
int menuBarHeight = Menu::getInstance()->geometry().height();
|
||||
int topMargin = titleBarHeight + menuBarHeight;
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FramelessDialog.h"
|
||||
#include "Menu.h"
|
||||
|
||||
const int RESIZE_HANDLE_WIDTH = 7;
|
||||
|
||||
|
@ -90,24 +94,27 @@ void FramelessDialog::showEvent(QShowEvent* event) {
|
|||
}
|
||||
|
||||
void FramelessDialog::resizeAndPosition(bool resizeParent) {
|
||||
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
|
||||
QSize parentSize = parentGeometry.size();
|
||||
|
||||
// keep full app height or width depending on position
|
||||
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
|
||||
setFixedHeight(parentWidget()->size().height());
|
||||
setFixedHeight(parentSize.height());
|
||||
} else {
|
||||
setFixedWidth(parentWidget()->size().width());
|
||||
setFixedWidth(parentSize.width());
|
||||
}
|
||||
|
||||
// resize parrent if width is smaller than this dialog
|
||||
if (resizeParent && parentWidget()->size().width() < size().width()) {
|
||||
parentWidget()->resize(size().width(), parentWidget()->size().height());
|
||||
if (resizeParent && parentSize.width() < size().width()) {
|
||||
parentWidget()->resize(size().width(), parentSize.height());
|
||||
}
|
||||
|
||||
if (_position == POSITION_LEFT || _position == POSITION_TOP) {
|
||||
// move to upper left corner
|
||||
move(parentWidget()->geometry().topLeft());
|
||||
move(parentGeometry.topLeft());
|
||||
} else if (_position == POSITION_RIGHT) {
|
||||
// move to upper right corner
|
||||
QPoint pos = parentWidget()->geometry().topRight();
|
||||
QPoint pos = parentGeometry.topRight();
|
||||
pos.setX(pos.x() - size().width());
|
||||
move(pos);
|
||||
}
|
||||
|
|
|
@ -67,83 +67,41 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) :
|
|||
|
||||
// watch for our dialog window moving screens. If it does we want to enforce our rules about
|
||||
// what screens we're allowed on
|
||||
QWindow* dialogWindow = windowHandle();
|
||||
connect(dialogWindow, &QWindow::screenChanged, this, &HMDToolsDialog::dialogWindowScreenChanged);
|
||||
connect(dialogWindow, &QWindow::xChanged, this, &HMDToolsDialog::dialogWindowGeometryChanged);
|
||||
connect(dialogWindow, &QWindow::yChanged, this, &HMDToolsDialog::dialogWindowGeometryChanged);
|
||||
connect(dialogWindow, &QWindow::widthChanged, this, &HMDToolsDialog::dialogWindowGeometryChanged);
|
||||
connect(dialogWindow, &QWindow::heightChanged, this, &HMDToolsDialog::dialogWindowGeometryChanged);
|
||||
watchWindow(windowHandle());
|
||||
if (Application::getInstance()->getRunningScriptsWidget()) {
|
||||
watchWindow(Application::getInstance()->getRunningScriptsWidget()->windowHandle());
|
||||
}
|
||||
if (Application::getInstance()->getToolWindow()) {
|
||||
watchWindow(Application::getInstance()->getToolWindow()->windowHandle());
|
||||
}
|
||||
if (Menu::getInstance()->getBandwidthDialog()) {
|
||||
watchWindow(Menu::getInstance()->getBandwidthDialog()->windowHandle());
|
||||
}
|
||||
if (Menu::getInstance()->getOctreeStatsDialog()) {
|
||||
watchWindow(Menu::getInstance()->getOctreeStatsDialog()->windowHandle());
|
||||
}
|
||||
if (Menu::getInstance()->getLodToolsDialog()) {
|
||||
watchWindow(Menu::getInstance()->getLodToolsDialog()->windowHandle());
|
||||
}
|
||||
|
||||
// when the application is about to quit, leave HDM mode
|
||||
connect(Application::getInstance(), SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
|
||||
|
||||
// keep track of changes to the number of screens
|
||||
connect(QApplication::desktop(), &QDesktopWidget::screenCountChanged, this, &HMDToolsDialog::screenCountChanged);
|
||||
|
||||
}
|
||||
|
||||
HMDToolsDialog::~HMDToolsDialog() {
|
||||
foreach(HMDWindowWatcher* watcher, _windowWatchers) {
|
||||
delete watcher;
|
||||
}
|
||||
_windowWatchers.clear();
|
||||
}
|
||||
|
||||
void HMDToolsDialog::applicationWindowScreenChanged(QScreen* screen) {
|
||||
_debugDetails->setText(getDebugDetails());
|
||||
}
|
||||
|
||||
void HMDToolsDialog::dialogWindowGeometryChanged(int arg) {
|
||||
QWindow* dialogWindow = windowHandle();
|
||||
_previousDialogRect = rect();
|
||||
_previousDialogRect = QRect(dialogWindow->mapToGlobal(_previousDialogRect.topLeft()),
|
||||
dialogWindow->mapToGlobal(_previousDialogRect.bottomRight()));
|
||||
_previousDialogScreen = dialogWindow->screen();
|
||||
}
|
||||
|
||||
void HMDToolsDialog::dialogWindowScreenChanged(QScreen* screen) {
|
||||
_debugDetails->setText(getDebugDetails());
|
||||
|
||||
// if we have more than one screen, and a known hmdScreen then try to
|
||||
// keep our dialog off of the hmdScreen
|
||||
if (QApplication::desktop()->screenCount() > 1) {
|
||||
|
||||
// we want to use a local variable here because we are not necesarily in HMD mode
|
||||
int hmdScreenNumber = OculusManager::getHMDScreen();
|
||||
if (_hmdScreenNumber >= 0) {
|
||||
QScreen* hmdScreen = QGuiApplication::screens()[hmdScreenNumber];
|
||||
if (screen == hmdScreen) {
|
||||
qDebug() << "HMD Tools: Whoa! What are you doing? You don't want to move me to the HMD Screen!";
|
||||
QWindow* dialogWindow = windowHandle();
|
||||
|
||||
// try to pick a better screen
|
||||
QScreen* betterScreen = NULL;
|
||||
|
||||
QWindow* appWindow = Application::getInstance()->getWindow()->windowHandle();
|
||||
QScreen* appScreen = appWindow->screen();
|
||||
|
||||
if (_previousDialogScreen && _previousDialogScreen != hmdScreen) {
|
||||
// first, if the previous dialog screen is not the HMD screen, then move it there.
|
||||
betterScreen = appScreen;
|
||||
} else if (appScreen != hmdScreen) {
|
||||
// second, if the application screen is not the HMD screen, then move it there.
|
||||
betterScreen = appScreen;
|
||||
} else if (_previousScreen && _previousScreen != hmdScreen) {
|
||||
// third, if the application screen is the HMD screen, we want to move it to
|
||||
// the previous screen
|
||||
betterScreen = _previousScreen;
|
||||
} else {
|
||||
// last, if we can't use the previous screen the use the primary desktop screen
|
||||
int desktopPrimaryScreenNumber = QApplication::desktop()->primaryScreen();
|
||||
QScreen* desktopPrimaryScreen = QGuiApplication::screens()[desktopPrimaryScreenNumber];
|
||||
betterScreen = desktopPrimaryScreen;
|
||||
}
|
||||
|
||||
if (betterScreen) {
|
||||
dialogWindow->setScreen(betterScreen);
|
||||
dialogWindow->setGeometry(_previousDialogRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString HMDToolsDialog::getDebugDetails() const {
|
||||
QString results;
|
||||
|
||||
|
@ -305,5 +263,79 @@ void HMDToolsDialog::screenCountChanged(int newCount) {
|
|||
_debugDetails->setText(getDebugDetails());
|
||||
}
|
||||
|
||||
void HMDToolsDialog::watchWindow(QWindow* window) {
|
||||
qDebug() << "HMDToolsDialog::watchWindow() window:" << window;
|
||||
if (window && !_windowWatchers.contains(window)) {
|
||||
HMDWindowWatcher* watcher = new HMDWindowWatcher(window, this);
|
||||
_windowWatchers[window] = watcher;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HMDWindowWatcher::HMDWindowWatcher(QWindow* window, HMDToolsDialog* hmdTools) :
|
||||
_window(window),
|
||||
_hmdTools(hmdTools),
|
||||
_previousScreen(NULL)
|
||||
{
|
||||
connect(window, &QWindow::screenChanged, this, &HMDWindowWatcher::windowScreenChanged);
|
||||
connect(window, &QWindow::xChanged, this, &HMDWindowWatcher::windowGeometryChanged);
|
||||
connect(window, &QWindow::yChanged, this, &HMDWindowWatcher::windowGeometryChanged);
|
||||
connect(window, &QWindow::widthChanged, this, &HMDWindowWatcher::windowGeometryChanged);
|
||||
connect(window, &QWindow::heightChanged, this, &HMDWindowWatcher::windowGeometryChanged);
|
||||
}
|
||||
|
||||
HMDWindowWatcher::~HMDWindowWatcher() {
|
||||
}
|
||||
|
||||
|
||||
void HMDWindowWatcher::windowGeometryChanged(int arg) {
|
||||
_previousRect = _window->geometry();
|
||||
_previousScreen = _window->screen();
|
||||
}
|
||||
|
||||
void HMDWindowWatcher::windowScreenChanged(QScreen* screen) {
|
||||
// if we have more than one screen, and a known hmdScreen then try to
|
||||
// keep our dialog off of the hmdScreen
|
||||
if (QApplication::desktop()->screenCount() > 1) {
|
||||
|
||||
// we want to use a local variable here because we are not necesarily in HMD mode
|
||||
int hmdScreenNumber = OculusManager::getHMDScreen();
|
||||
if (hmdScreenNumber >= 0) {
|
||||
QScreen* hmdScreen = QGuiApplication::screens()[hmdScreenNumber];
|
||||
if (screen == hmdScreen) {
|
||||
qDebug() << "HMD Tools: Whoa! What are you doing? You don't want to move me to the HMD Screen!";
|
||||
|
||||
// try to pick a better screen
|
||||
QScreen* betterScreen = NULL;
|
||||
|
||||
QScreen* lastApplicationScreen = _hmdTools->getLastApplicationScreen();
|
||||
QWindow* appWindow = Application::getInstance()->getWindow()->windowHandle();
|
||||
QScreen* appScreen = appWindow->screen();
|
||||
|
||||
if (_previousScreen && _previousScreen != hmdScreen) {
|
||||
// first, if the previous dialog screen is not the HMD screen, then move it there.
|
||||
betterScreen = _previousScreen;
|
||||
} else if (appScreen != hmdScreen) {
|
||||
// second, if the application screen is not the HMD screen, then move it there.
|
||||
betterScreen = appScreen;
|
||||
} else if (lastApplicationScreen && lastApplicationScreen != hmdScreen) {
|
||||
// third, if the application screen is the HMD screen, we want to move it to
|
||||
// the previous screen
|
||||
betterScreen = lastApplicationScreen;
|
||||
} else {
|
||||
// last, if we can't use the previous screen the use the primary desktop screen
|
||||
int desktopPrimaryScreenNumber = QApplication::desktop()->primaryScreen();
|
||||
QScreen* desktopPrimaryScreen = QGuiApplication::screens()[desktopPrimaryScreenNumber];
|
||||
betterScreen = desktopPrimaryScreen;
|
||||
}
|
||||
|
||||
if (betterScreen) {
|
||||
_window->setScreen(betterScreen);
|
||||
_window->setGeometry(_previousRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <QDialog>
|
||||
|
||||
class HMDWindowWatcher;
|
||||
|
||||
class HMDToolsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -22,6 +24,10 @@ public:
|
|||
~HMDToolsDialog();
|
||||
|
||||
QString getDebugDetails() const;
|
||||
QScreen* getHMDScreen() const { return _hmdScreen; }
|
||||
QScreen* getLastApplicationScreen() const { return _previousScreen; }
|
||||
bool hasHMDScreen() const { return _hmdScreenNumber >= -1; }
|
||||
void watchWindow(QWindow* window);
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
@ -32,8 +38,6 @@ public slots:
|
|||
void activateWindowAfterEnterMode();
|
||||
void moveWindowAfterLeaveMode();
|
||||
void applicationWindowScreenChanged(QScreen* screen);
|
||||
void dialogWindowScreenChanged(QScreen* screen);
|
||||
void dialogWindowGeometryChanged(int arg);
|
||||
void aboutToQuit();
|
||||
void screenCountChanged(int newCount);
|
||||
|
||||
|
@ -58,6 +62,27 @@ private:
|
|||
QRect _previousDialogRect;
|
||||
QScreen* _previousDialogScreen;
|
||||
bool _inHDMMode;
|
||||
|
||||
QHash<QWindow*, HMDWindowWatcher*> _windowWatchers;
|
||||
};
|
||||
|
||||
|
||||
class HMDWindowWatcher : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Sets up the UI
|
||||
HMDWindowWatcher(QWindow* window, HMDToolsDialog* hmdTools);
|
||||
~HMDWindowWatcher();
|
||||
|
||||
public slots:
|
||||
void windowScreenChanged(QScreen* screen);
|
||||
void windowGeometryChanged(int arg);
|
||||
|
||||
private:
|
||||
QWindow* _window;
|
||||
HMDToolsDialog* _hmdTools;
|
||||
QRect _previousRect;
|
||||
QScreen* _previousScreen;
|
||||
};
|
||||
|
||||
#endif // hifi_HMDToolsDialog_h
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#include <QFileInfo>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QWindow>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
|
@ -82,10 +84,6 @@ void RunningScriptsWidget::loadSelectedScript() {
|
|||
}
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::setBoundary(const QRect& rect) {
|
||||
_boundary = rect;
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
||||
setUpdatesEnabled(false);
|
||||
QLayoutItem* widget;
|
||||
|
@ -153,7 +151,7 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) {
|
|||
ui->filterLineEdit->setFocus();
|
||||
}
|
||||
|
||||
const QRect parentGeometry = parentWidget()->geometry();
|
||||
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
|
||||
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
|
||||
int menuBarHeight = Menu::getInstance()->geometry().height();
|
||||
int topMargin = titleBarHeight + menuBarHeight;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
#include "FramelessDialog.h"
|
||||
#include "ScriptsTableWidget.h"
|
||||
|
||||
namespace Ui {
|
||||
|
@ -44,7 +43,6 @@ protected:
|
|||
|
||||
public slots:
|
||||
void scriptStopped(const QString& scriptName);
|
||||
void setBoundary(const QRect& rect);
|
||||
|
||||
private slots:
|
||||
void allScriptsStopped();
|
||||
|
@ -61,7 +59,6 @@ private:
|
|||
ScriptsTableWidget* _recentlyLoadedScriptsTable;
|
||||
QStringList _recentlyLoadedScripts;
|
||||
QString _lastStoppedScript;
|
||||
QRect _boundary;
|
||||
};
|
||||
|
||||
#endif // hifi_RunningScriptsWidget_h
|
||||
|
|
|
@ -477,10 +477,6 @@ void Stats::display(
|
|||
VoxelSystem* voxels = Application::getInstance()->getVoxels();
|
||||
|
||||
lines = _expanded ? 14 : 3;
|
||||
bool wantSpatialProcessing = false; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)
|
||||
if (_expanded && wantSpatialProcessing) {
|
||||
lines += 10; // spatial audio processing adds 1 spacing line and 8 extra lines of info
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset,
|
||||
lines * STATS_PELS_PER_LINE + 10);
|
||||
|
@ -669,104 +665,6 @@ void Stats::display(
|
|||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
if (_expanded && wantSpatialProcessing) {
|
||||
verticalOffset += STATS_PELS_PER_LINE; // space one line...
|
||||
|
||||
const AudioReflector* audioReflector = Application::getInstance()->getAudioReflector();
|
||||
|
||||
// add some reflection stats
|
||||
char reflectionsStatus[128];
|
||||
|
||||
bool includeOriginal = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingIncludeOriginal)
|
||||
bool separateEars = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSeparateEars)
|
||||
bool stereoSource = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingStereoSource)
|
||||
bool randomSurfaces = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingSlightlyRandomSurfaces)
|
||||
|
||||
sprintf(reflectionsStatus, "Reflections: %d, Original: %s, Ears: %s, Source: %s, Normals: %s",
|
||||
audioReflector->getReflections(),
|
||||
(includeOriginal ? "included" : "silent"),
|
||||
(separateEars ? "two" : "one"),
|
||||
(stereoSource ? "stereo" : "mono"),
|
||||
(randomSurfaces ? "random" : "regular")
|
||||
);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
bool wantPreDelay = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay)
|
||||
float preDelay = wantPreDelay ? audioReflector->getPreDelay() : 0.0f;
|
||||
|
||||
sprintf(reflectionsStatus, "Delay: pre: %6.3f, average %6.3f, max %6.3f, min %6.3f, speed: %6.3f",
|
||||
preDelay,
|
||||
audioReflector->getAverageDelayMsecs(),
|
||||
audioReflector->getMaxDelayMsecs(),
|
||||
audioReflector->getMinDelayMsecs(),
|
||||
audioReflector->getSoundMsPerMeter());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
//Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
|
||||
bool distanceAttenuationDisabled = false;
|
||||
|
||||
// Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingAlternateDistanceAttenuate);
|
||||
bool alternateDistanceAttenuationEnabled = false;
|
||||
|
||||
sprintf(reflectionsStatus, "Attenuation: average %5.3f, max %5.3f, min %5.3f, %s: %5.3f",
|
||||
audioReflector->getAverageAttenuation(),
|
||||
audioReflector->getMaxAttenuation(),
|
||||
audioReflector->getMinAttenuation(),
|
||||
(distanceAttenuationDisabled ? "Distance Factor [DISABLED]" :
|
||||
alternateDistanceAttenuationEnabled ? "Distance Factor [ALTERNATE]" : "Distance Factor [STANARD]"),
|
||||
audioReflector->getDistanceAttenuationScalingFactor());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
bool localAudio = true; // Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio);
|
||||
sprintf(reflectionsStatus, "Local Audio: %s Attenuation: %5.3f", (localAudio ? "yes" : "no"),
|
||||
audioReflector->getLocalAudioAttenuationFactor());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
bool diffusionEnabled = true; //Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
|
||||
int fanout = diffusionEnabled ? audioReflector->getDiffusionFanout() : 0;
|
||||
int diffusionPaths = diffusionEnabled ? audioReflector->getDiffusionPathCount() : 0;
|
||||
sprintf(reflectionsStatus, "Diffusion: %s, Fanout: %d, Paths: %d",
|
||||
(diffusionEnabled ? "yes" : "no"), fanout, diffusionPaths);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
const float AS_PERCENT = 100.0f;
|
||||
float reflectiveRatio = audioReflector->getReflectiveRatio() * AS_PERCENT;
|
||||
float diffusionRatio = audioReflector->getDiffusionRatio() * AS_PERCENT;
|
||||
float absorptionRatio = audioReflector->getAbsorptionRatio() * AS_PERCENT;
|
||||
sprintf(reflectionsStatus, "Ratios: Reflective: %5.3f, Diffusion: %5.3f, Absorption: %5.3f",
|
||||
reflectiveRatio, diffusionRatio, absorptionRatio);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
sprintf(reflectionsStatus, "Comb Filter Window: %5.3f ms, Allowed: %d, Suppressed: %d",
|
||||
audioReflector->getCombFilterWindow(),
|
||||
audioReflector->getEchoesInjected(),
|
||||
audioReflector->getEchoesSuppressed());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
|
||||
|
||||
sprintf(reflectionsStatus, "Wet/Dry Mix: Original: %5.3f Echoes: %5.3f",
|
||||
audioReflector->getOriginalSourceAttenuation(),
|
||||
audioReflector->getEchoesAttenuation());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, 0.10f, 0.0f, 2.0f, reflectionsStatus, color);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Stats::setMetavoxelStats(int internal, int leaves, int sendProgress,
|
||||
|
|
|
@ -432,19 +432,19 @@ bool Overlays::isLoaded(unsigned int id) {
|
|||
return thisOverlay->isLoaded();
|
||||
}
|
||||
|
||||
float Overlays::textWidth(unsigned int id, const QString& text) const {
|
||||
QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
|
||||
Overlay* thisOverlay = _overlays2D[id];
|
||||
if (thisOverlay) {
|
||||
if (typeid(*thisOverlay) == typeid(TextOverlay)) {
|
||||
return static_cast<TextOverlay*>(thisOverlay)->textWidth(text);
|
||||
return static_cast<TextOverlay*>(thisOverlay)->textSize(text);
|
||||
}
|
||||
} else {
|
||||
thisOverlay = _overlays3D[id];
|
||||
if (thisOverlay) {
|
||||
if (typeid(*thisOverlay) == typeid(Text3DOverlay)) {
|
||||
return static_cast<Text3DOverlay*>(thisOverlay)->textWidth(text);
|
||||
return static_cast<Text3DOverlay*>(thisOverlay)->textSize(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
return QSizeF(0.0f, 0.0f);
|
||||
}
|
||||
|
|
|
@ -85,9 +85,9 @@ public slots:
|
|||
/// returns whether the overlay's assets are loaded or not
|
||||
bool isLoaded(unsigned int id);
|
||||
|
||||
/// returns the width of the given text in the specified overlay if it is a text overlay: in pixels if it is a 2D text
|
||||
/// returns the size of the given text in the specified overlay if it is a text overlay: in pixels if it is a 2D text
|
||||
/// overlay; in meters if it is a 3D text overlay
|
||||
float textWidth(unsigned int id, const QString& text) const;
|
||||
QSizeF textSize(unsigned int id, const QString& text) const;
|
||||
|
||||
private:
|
||||
QMap<unsigned int, Overlay*> _overlays2D;
|
||||
|
|
|
@ -108,7 +108,7 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
|
||||
|
||||
// Same font properties as textWidth()
|
||||
// Same font properties as textSize()
|
||||
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
|
||||
float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO;
|
||||
|
||||
|
@ -236,11 +236,23 @@ Text3DOverlay* Text3DOverlay::createClone() const {
|
|||
return new Text3DOverlay(this);;
|
||||
}
|
||||
|
||||
float Text3DOverlay::textWidth(const QString& text) const {
|
||||
QSizeF Text3DOverlay::textSize(const QString& text) const {
|
||||
|
||||
QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render()
|
||||
QFontMetrics fontMetrics(font);
|
||||
float scaleFactor = _lineHeight * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE;
|
||||
return scaleFactor * (float)fontMetrics.width(qPrintable(text));
|
||||
const float TEXT_SCALE_ADJUST = 1.02f; // Experimentally detemined for the specified font
|
||||
const int TEXT_HEIGHT_ADJUST = -6;
|
||||
float scaleFactor = _lineHeight * TEXT_SCALE_ADJUST * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE;
|
||||
|
||||
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));
|
||||
|
||||
float width = 0.0f;
|
||||
for (int i = 0; i < lines.count(); i += 1) {
|
||||
width = std::max(width, scaleFactor * (float)fontMetrics.width(qPrintable(lines[i])));
|
||||
}
|
||||
|
||||
float height = lines.count() * scaleFactor * (float)(fontMetrics.height() + TEXT_HEIGHT_ADJUST);
|
||||
|
||||
return QSizeF(width, height);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
float textWidth(const QString& text) const; // Meters
|
||||
QSizeF textSize(const QString& test) const; // Meters
|
||||
|
||||
virtual Text3DOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ void TextOverlay::render(RenderArgs* args) {
|
|||
glVertex2f(_bounds.left(), _bounds.bottom());
|
||||
glEnd();
|
||||
|
||||
// Same font properties as textWidth()
|
||||
// Same font properties as textSize()
|
||||
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
|
||||
|
||||
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
|
||||
|
@ -169,8 +169,20 @@ QScriptValue TextOverlay::getProperty(const QString& property) {
|
|||
return Overlay2D::getProperty(property);
|
||||
}
|
||||
|
||||
float TextOverlay::textWidth(const QString& text) const {
|
||||
QSizeF TextOverlay::textSize(const QString& text) const {
|
||||
|
||||
QFont font(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); // Same font properties as render()
|
||||
QFontMetrics fontMetrics(font);
|
||||
return fontMetrics.width(qPrintable(text));
|
||||
const int TEXT_HEIGHT_ADJUST = -2; // Experimentally determined for the specified font
|
||||
|
||||
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));
|
||||
|
||||
int width = 0;
|
||||
for (int i = 0; i < lines.count(); i += 1) {
|
||||
width = std::max(width, fontMetrics.width(qPrintable(lines[i])));
|
||||
}
|
||||
|
||||
int height = lines.count() * (fontMetrics.height() + TEXT_HEIGHT_ADJUST);
|
||||
|
||||
return QSizeF(width, height);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
virtual TextOverlay* createClone() const;
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
float textWidth(const QString& text) const; // Pixels
|
||||
QSizeF textSize(const QString& test) const; // Pixels
|
||||
|
||||
private:
|
||||
QString _text;
|
||||
|
|
|
@ -1,742 +0,0 @@
|
|||
//
|
||||
// PrimitiveRenderer.cpp
|
||||
// interface/src/voxels
|
||||
//
|
||||
// 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 <QMutexLocker>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "OctreeElement.h"
|
||||
#include "PrimitiveRenderer.h"
|
||||
|
||||
Primitive::Primitive() {
|
||||
}
|
||||
|
||||
Primitive::~Primitive() {
|
||||
}
|
||||
|
||||
// Simple dispatch between API and SPI
|
||||
|
||||
const VertexElementList& Primitive::vertexElements() const {
|
||||
return vVertexElements();
|
||||
}
|
||||
|
||||
VertexElementIndexList& Primitive::vertexElementIndices() {
|
||||
return vVertexElementIndices();
|
||||
}
|
||||
|
||||
TriElementList& Primitive::triElements() {
|
||||
return vTriElements();
|
||||
}
|
||||
|
||||
void Primitive::releaseVertexElements() {
|
||||
vReleaseVertexElements();
|
||||
}
|
||||
|
||||
unsigned long Primitive::getMemoryUsage() {
|
||||
return vGetMemoryUsage();
|
||||
}
|
||||
|
||||
|
||||
Cube::Cube(
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float s,
|
||||
unsigned char r,
|
||||
unsigned char g,
|
||||
unsigned char b,
|
||||
unsigned char faceExclusions
|
||||
) :
|
||||
_cpuMemoryUsage(0) {
|
||||
init(x, y, z, s, r, g, b, faceExclusions);
|
||||
}
|
||||
|
||||
Cube::~Cube() {
|
||||
terminate();
|
||||
}
|
||||
|
||||
void Cube::init(
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float s,
|
||||
unsigned char r,
|
||||
unsigned char g,
|
||||
unsigned char b,
|
||||
unsigned char faceExclusions
|
||||
) {
|
||||
|
||||
initializeVertices(x, y, z, s, r, g, b, faceExclusions);
|
||||
initializeTris(faceExclusions);
|
||||
}
|
||||
|
||||
void Cube::terminate() {
|
||||
|
||||
terminateTris();
|
||||
terminateVertices();
|
||||
}
|
||||
|
||||
void Cube::initializeVertices(
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float s,
|
||||
unsigned char r,
|
||||
unsigned char g,
|
||||
unsigned char b,
|
||||
unsigned char faceExclusions
|
||||
) {
|
||||
|
||||
for (int i = 0; i < _sNumVerticesPerCube; i++) {
|
||||
// Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask.
|
||||
// uncomment this line to load all faces: if (~0x00 & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
||||
// uncomment this line to include shared faces: if (faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
||||
// uncomment this line to exclude shared faces:
|
||||
if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) {
|
||||
|
||||
VertexElement* v = new VertexElement();
|
||||
if (v) {
|
||||
// Construct vertex position
|
||||
v->position.x = x + s * _sVertexIndexToConstructionVector[i][0];
|
||||
v->position.y = y + s * _sVertexIndexToConstructionVector[i][1];
|
||||
v->position.z = z + s * _sVertexIndexToConstructionVector[i][2];
|
||||
|
||||
// Construct vertex normal
|
||||
v->normal.x = _sVertexIndexToNormalVector[i >> 2][0];
|
||||
v->normal.y = _sVertexIndexToNormalVector[i >> 2][1];
|
||||
v->normal.z = _sVertexIndexToNormalVector[i >> 2][2];
|
||||
|
||||
// Construct vertex color
|
||||
//#define FALSE_COLOR
|
||||
#ifndef FALSE_COLOR
|
||||
v->color.r = r;
|
||||
v->color.g = g;
|
||||
v->color.b = b;
|
||||
v->color.a = 255;
|
||||
#else
|
||||
static unsigned char falseColor[6][3] = {
|
||||
192, 0, 0, // Bot
|
||||
0, 192, 0, // Top
|
||||
0, 0, 192, // Right
|
||||
192, 0, 192, // Left
|
||||
192, 192, 0, // Near
|
||||
192, 192, 192 // Far
|
||||
};
|
||||
v->color.r = falseColor[i >> 2][0];
|
||||
v->color.g = falseColor[i >> 2][1];
|
||||
v->color.b = falseColor[i >> 2][2];
|
||||
v->color.a = 255;
|
||||
#endif
|
||||
|
||||
// Add vertex element to list
|
||||
_vertices.push_back(v);
|
||||
_cpuMemoryUsage += sizeof(VertexElement);
|
||||
_cpuMemoryUsage += sizeof(VertexElement*);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cube::terminateVertices() {
|
||||
|
||||
for (VertexElementList::iterator it = _vertices.begin(); it != _vertices.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
_cpuMemoryUsage -= _vertices.size() * (sizeof(VertexElement) + sizeof(VertexElement*));
|
||||
_vertices.clear();
|
||||
}
|
||||
|
||||
void Cube::initializeTris(
|
||||
unsigned char faceExclusions
|
||||
) {
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < _sNumFacesPerCube; i++) {
|
||||
// Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask.
|
||||
// uncomment this line to load all faces: if (~0x00 & _sFaceIndexToHalfSpaceMask[i]) {
|
||||
// uncomment this line to include shared faces: if (faceExclusions & _sFaceIndexToHalfSpaceMask[i]) {
|
||||
// uncomment this line to exclude shared faces:
|
||||
if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i]) {
|
||||
|
||||
int start = index;
|
||||
// Create the triangulated face, two tris, six indices referencing four vertices, both
|
||||
// with cw winding order, such that:
|
||||
|
||||
// A-B
|
||||
// |\|
|
||||
// D-C
|
||||
|
||||
// Store triangle ABC
|
||||
|
||||
TriElement* tri = new TriElement();
|
||||
if (tri) {
|
||||
tri->indices[0] = index++;
|
||||
tri->indices[1] = index++;
|
||||
tri->indices[2] = index;
|
||||
|
||||
// Add tri element to list
|
||||
_tris.push_back(tri);
|
||||
_cpuMemoryUsage += sizeof(TriElement);
|
||||
_cpuMemoryUsage += sizeof(TriElement*);
|
||||
}
|
||||
|
||||
// Now store triangle ACD
|
||||
tri = new TriElement();
|
||||
if (tri) {
|
||||
tri->indices[0] = start;
|
||||
tri->indices[1] = index++;
|
||||
tri->indices[2] = index++;
|
||||
|
||||
// Add tri element to list
|
||||
_tris.push_back(tri);
|
||||
_cpuMemoryUsage += sizeof(TriElement);
|
||||
_cpuMemoryUsage += sizeof(TriElement*);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cube::terminateTris() {
|
||||
|
||||
for (TriElementList::iterator it = _tris.begin(); it != _tris.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
_cpuMemoryUsage -= _tris.size() * (sizeof(TriElement) + sizeof(TriElement*));
|
||||
_tris.clear();
|
||||
}
|
||||
|
||||
const VertexElementList& Cube::vVertexElements() const {
|
||||
return _vertices;
|
||||
}
|
||||
|
||||
VertexElementIndexList& Cube::vVertexElementIndices() {
|
||||
return _vertexIndices;
|
||||
}
|
||||
|
||||
TriElementList& Cube::vTriElements() {
|
||||
return _tris;
|
||||
}
|
||||
|
||||
void Cube::vReleaseVertexElements() {
|
||||
terminateVertices();
|
||||
}
|
||||
|
||||
unsigned long Cube::vGetMemoryUsage() {
|
||||
return _cpuMemoryUsage;
|
||||
}
|
||||
|
||||
unsigned char Cube::_sFaceIndexToHalfSpaceMask[6] = {
|
||||
OctreeElement::HalfSpace::Bottom,
|
||||
OctreeElement::HalfSpace::Top,
|
||||
OctreeElement::HalfSpace::Right,
|
||||
OctreeElement::HalfSpace::Left,
|
||||
OctreeElement::HalfSpace::Near,
|
||||
OctreeElement::HalfSpace::Far,
|
||||
};
|
||||
|
||||
// Construction vectors ordered such that the vertices of each face are
|
||||
// clockwise in a right-handed coordinate system with B-L-N at 0,0,0.
|
||||
float Cube::_sVertexIndexToConstructionVector[24][3] = {
|
||||
// Bottom
|
||||
{ 0,0,0 },
|
||||
{ 1,0,0 },
|
||||
{ 1,0,1 },
|
||||
{ 0,0,1 },
|
||||
// Top
|
||||
{ 0,1,0 },
|
||||
{ 0,1,1 },
|
||||
{ 1,1,1 },
|
||||
{ 1,1,0 },
|
||||
// Right
|
||||
{ 1,0,0 },
|
||||
{ 1,1,0 },
|
||||
{ 1,1,1 },
|
||||
{ 1,0,1 },
|
||||
// Left
|
||||
{ 0,0,0 },
|
||||
{ 0,0,1 },
|
||||
{ 0,1,1 },
|
||||
{ 0,1,0 },
|
||||
// Near
|
||||
{ 0,0,0 },
|
||||
{ 0,1,0 },
|
||||
{ 1,1,0 },
|
||||
{ 1,0,0 },
|
||||
// Far
|
||||
{ 0,0,1 },
|
||||
{ 1,0,1 },
|
||||
{ 1,1,1 },
|
||||
{ 0,1,1 },
|
||||
};
|
||||
|
||||
// Normals for a right-handed coordinate system
|
||||
float Cube::_sVertexIndexToNormalVector[6][3] = {
|
||||
{ 0,-1, 0 }, // Bottom
|
||||
{ 0, 1, 0 }, // Top
|
||||
{ 1, 0, 0 }, // Right
|
||||
{ -1, 0, 0 }, // Left
|
||||
{ 0, 0,-1 }, // Near
|
||||
{ 0, 0, 1 }, // Far
|
||||
};
|
||||
|
||||
Renderer::Renderer() {
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
}
|
||||
|
||||
// Simple dispatch between API and SPI
|
||||
int Renderer::add(
|
||||
Primitive* primitive
|
||||
) {
|
||||
return vAdd(primitive);
|
||||
}
|
||||
|
||||
void Renderer::remove(
|
||||
int id
|
||||
) {
|
||||
vRemove(id);
|
||||
}
|
||||
|
||||
void Renderer::release() {
|
||||
vRelease();
|
||||
}
|
||||
|
||||
void Renderer::render() {
|
||||
vRender();
|
||||
}
|
||||
|
||||
unsigned long Renderer::getMemoryUsage() {
|
||||
return vGetMemoryUsage();
|
||||
}
|
||||
|
||||
unsigned long Renderer::getMemoryUsageGPU() {
|
||||
return vGetMemoryUsageGPU();
|
||||
}
|
||||
|
||||
PrimitiveRenderer::PrimitiveRenderer(
|
||||
int maxCount
|
||||
) :
|
||||
_maxCount(maxCount),
|
||||
_triBufferId(0),
|
||||
_vertexBufferId(0),
|
||||
|
||||
_vertexElementCount(0),
|
||||
_maxVertexElementCount(0),
|
||||
|
||||
_triElementCount(0),
|
||||
_maxTriElementCount(0),
|
||||
|
||||
_primitives(),
|
||||
_primitiveCount(0),
|
||||
|
||||
_gpuMemoryUsage(0),
|
||||
_cpuMemoryUsage(0)
|
||||
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
PrimitiveRenderer::~PrimitiveRenderer() {
|
||||
|
||||
terminate();
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::init() {
|
||||
|
||||
initializeGL();
|
||||
initializeBookkeeping();
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::initializeGL() {
|
||||
|
||||
glGenBuffers(1, &_triBufferId);
|
||||
glGenBuffers(1, &_vertexBufferId);
|
||||
|
||||
// Set up the element array buffer containing the index ids
|
||||
_maxTriElementCount = _maxCount * 2;
|
||||
int size = _maxTriElementCount * _sIndicesPerTri * sizeof(GLint);
|
||||
_gpuMemoryUsage += size;
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
// Set up the array buffer in the form of array of structures
|
||||
// I chose AOS because it maximizes the amount of data tranferred
|
||||
// by a single glBufferSubData call.
|
||||
_maxVertexElementCount = _maxCount * 8;
|
||||
size = _maxVertexElementCount * sizeof(VertexElement);
|
||||
_gpuMemoryUsage += size;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
||||
glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Initialize the first tri element in the buffer to all zeros, the
|
||||
// degenerate case
|
||||
deconstructTriElement(0);
|
||||
|
||||
// Initialize the first vertex element in the buffer to all zeros, the
|
||||
// degenerate case
|
||||
deconstructVertexElement(0);
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::initializeBookkeeping() {
|
||||
|
||||
// Start primitive count at one, because zero is reserved for the degenerate triangle
|
||||
_primitives.resize(_maxCount + 1);
|
||||
|
||||
// Set the counters
|
||||
_primitiveCount = 1;
|
||||
_vertexElementCount = 1;
|
||||
_triElementCount = 1;
|
||||
|
||||
// Guesstimate the memory consumption
|
||||
_cpuMemoryUsage = sizeof(PrimitiveRenderer);
|
||||
_cpuMemoryUsage += _availablePrimitiveIndex.capacity() * sizeof(int);
|
||||
_cpuMemoryUsage += _availableVertexElementIndex.capacity() * sizeof(int);
|
||||
_cpuMemoryUsage += _availableTriElementIndex.capacity() * sizeof(int);
|
||||
_cpuMemoryUsage += _deconstructTriElementIndex.capacity() * sizeof(int);
|
||||
_cpuMemoryUsage += _constructPrimitiveIndex.capacity() * sizeof(int);
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::terminate() {
|
||||
|
||||
terminateBookkeeping();
|
||||
terminateGL();
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::terminateGL() {
|
||||
|
||||
if (_vertexBufferId) {
|
||||
glDeleteBuffers(1, &_vertexBufferId);
|
||||
_vertexBufferId = 0;
|
||||
}
|
||||
|
||||
if (_triBufferId) {
|
||||
glDeleteBuffers(1, &_triBufferId);
|
||||
_triBufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::terminateBookkeeping() {
|
||||
|
||||
// Delete all of the primitives
|
||||
for (int i = _primitiveCount + 1; --i > 0; ) {
|
||||
Primitive* primitive = _primitives[i];
|
||||
if (primitive) {
|
||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
||||
_primitives[i] = 0;
|
||||
delete primitive;
|
||||
}
|
||||
}
|
||||
|
||||
// Drain the queues
|
||||
_availablePrimitiveIndex.clear();
|
||||
_availableVertexElementIndex.clear();
|
||||
_availableTriElementIndex.clear();
|
||||
_deconstructTriElementIndex.clear();
|
||||
_constructPrimitiveIndex.clear();
|
||||
|
||||
_cpuMemoryUsage = sizeof(PrimitiveRenderer) + _primitives.size() * sizeof(Primitive *);
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::constructElements(
|
||||
Primitive* primitive
|
||||
) {
|
||||
|
||||
// Load vertex elements
|
||||
VertexElementIndexList& vertexElementIndexList = primitive->vertexElementIndices();
|
||||
const VertexElementList& vertices = primitive->vertexElements();
|
||||
{
|
||||
for (VertexElementList::const_iterator it = vertices.begin(); it != vertices.end(); ++it ) {
|
||||
int index = getAvailableVertexElementIndex();
|
||||
if (index != 0) {
|
||||
// Store the vertex element index in the primitive's
|
||||
// vertex element index list
|
||||
vertexElementIndexList.push_back(index);
|
||||
|
||||
VertexElement* vertex = *it;
|
||||
transferVertexElement(index, vertex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load tri elements
|
||||
if (vertexElementIndexList.size() == vertices.size()) {
|
||||
TriElementList& tris = primitive->triElements();
|
||||
|
||||
for (TriElementList::iterator it = tris.begin(); it != tris.end(); ++it) {
|
||||
TriElement* tri = *it;
|
||||
int index = getAvailableTriElementIndex();
|
||||
if (index != 0) {
|
||||
int k;
|
||||
k = tri->indices[0];
|
||||
tri->indices[0] = vertexElementIndexList[k];
|
||||
|
||||
k = tri->indices[1];
|
||||
tri->indices[1] = vertexElementIndexList[k];
|
||||
|
||||
k = tri->indices[2];
|
||||
tri->indices[2] = vertexElementIndexList[k];
|
||||
|
||||
tri->id = index;
|
||||
transferTriElement(index, tri->indices);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: failure mode
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::deconstructElements(
|
||||
Primitive* primitive
|
||||
) {
|
||||
|
||||
// Schedule the tri elements of the face for deconstruction
|
||||
{
|
||||
TriElementList& tris = primitive->triElements();
|
||||
|
||||
for (TriElementList::const_iterator it = tris.begin(); it != tris.end(); ++it) {
|
||||
const TriElement* tri = *it;
|
||||
|
||||
if (tri->id) {
|
||||
// Put the tri element index into decon queue
|
||||
_deconstructTriElementIndex.push(tri->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the vertex element index to the available queue, it is not necessary
|
||||
// to zero the data
|
||||
{
|
||||
VertexElementIndexList& vertexIndexList = primitive->vertexElementIndices();
|
||||
|
||||
for (VertexElementIndexList::const_iterator it = vertexIndexList.begin(); it != vertexIndexList.end(); ++it) {
|
||||
int index = *it;
|
||||
|
||||
if (index) {
|
||||
// Put the vertex element index into the available queue
|
||||
_availableVertexElementIndex.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete primitive;
|
||||
}
|
||||
|
||||
int PrimitiveRenderer::getAvailablePrimitiveIndex() {
|
||||
|
||||
int index;
|
||||
|
||||
// Check the available primitive index queue first for an available index.
|
||||
if (!_availablePrimitiveIndex.isEmpty()) {
|
||||
index = _availablePrimitiveIndex.pop();
|
||||
} else if (_primitiveCount < _maxCount) {
|
||||
// There are no primitive indices available from the queue,
|
||||
// make one up
|
||||
index = _primitiveCount++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int PrimitiveRenderer::getAvailableVertexElementIndex() {
|
||||
|
||||
int index;
|
||||
|
||||
// Check the available vertex element queue first for an available index.
|
||||
if (!_availableVertexElementIndex.isEmpty()) {
|
||||
index = _availableVertexElementIndex.pop();
|
||||
} else if (_vertexElementCount < _maxVertexElementCount) {
|
||||
// There are no vertex elements available from the queue,
|
||||
// grab one from the end of the list
|
||||
index = _vertexElementCount++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int PrimitiveRenderer::getAvailableTriElementIndex() {
|
||||
|
||||
int index;
|
||||
|
||||
// Check the tri elements scheduled for deconstruction queue first to
|
||||
// intercept and reuse an index without it having to be destroyed
|
||||
if (!_deconstructTriElementIndex.isEmpty()) {
|
||||
index = _deconstructTriElementIndex.pop();
|
||||
} else if (!_availableTriElementIndex.isEmpty()) {
|
||||
// Nothing available in the deconstruction queue, now
|
||||
// check the available tri element queue for an available index.
|
||||
index = _availableTriElementIndex.pop();
|
||||
} else if (_triElementCount < _maxTriElementCount) {
|
||||
// There are no reusable tri elements available from the queue,
|
||||
// grab one from the end of the list
|
||||
index = _triElementCount++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::deconstructTriElement(
|
||||
int idx
|
||||
) {
|
||||
|
||||
// Set the tri element to the degenerate case.
|
||||
static int degenerate[3] = { 0, 0, 0 };
|
||||
transferTriElement(idx, degenerate);
|
||||
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::deconstructVertexElement(
|
||||
int idx
|
||||
) {
|
||||
|
||||
// Set the vertex element to the degenerate case.
|
||||
VertexElement degenerate;
|
||||
memset(°enerate, 0, sizeof(degenerate));
|
||||
|
||||
transferVertexElement(idx, °enerate);
|
||||
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::transferVertexElement(
|
||||
int idx,
|
||||
VertexElement* vertex
|
||||
) {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, idx * sizeof(VertexElement), sizeof(VertexElement), vertex);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::transferTriElement(
|
||||
int idx,
|
||||
int tri[3]
|
||||
) {
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, idx * _sBytesPerTriElement, _sBytesPerTriElement, tri);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
int PrimitiveRenderer::vAdd(
|
||||
Primitive* primitive
|
||||
) {
|
||||
|
||||
QMutexLocker lock(&_guard);
|
||||
int id = getAvailablePrimitiveIndex();
|
||||
if (id != 0) {
|
||||
// Take ownership of primitive, including responsibility
|
||||
// for destruction
|
||||
_primitives[id] = primitive;
|
||||
_constructPrimitiveIndex.push(id);
|
||||
_cpuMemoryUsage += primitive->getMemoryUsage();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::vRemove(
|
||||
int id
|
||||
) {
|
||||
|
||||
if (id != 0) {
|
||||
QMutexLocker lock(&_guard);
|
||||
|
||||
// Locate and remove the primitive by id in the vector map
|
||||
Primitive* primitive = _primitives[id];
|
||||
if (primitive) {
|
||||
_primitives[id] = 0;
|
||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
||||
deconstructElements(primitive);
|
||||
|
||||
// Queue the index onto the available primitive stack.
|
||||
_availablePrimitiveIndex.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::vRelease() {
|
||||
|
||||
QMutexLocker lock(&_guard);
|
||||
|
||||
terminateBookkeeping();
|
||||
initializeBookkeeping();
|
||||
}
|
||||
|
||||
void PrimitiveRenderer::vRender() {
|
||||
|
||||
int id;
|
||||
QMutexLocker lock(&_guard);
|
||||
|
||||
// Iterate over the set of triangle element array buffer ids scheduled for
|
||||
// destruction. Set the triangle element to the degenerate case. Queue the id
|
||||
// onto the available tri element stack.
|
||||
while (!_deconstructTriElementIndex.isEmpty()) {
|
||||
id = _deconstructTriElementIndex.pop();
|
||||
deconstructTriElement(id);
|
||||
_availableTriElementIndex.push(id);
|
||||
}
|
||||
|
||||
// Iterate over the set of primitive ids scheduled for construction. Transfer
|
||||
// primitive data to the GPU.
|
||||
while (!_constructPrimitiveIndex.isEmpty()) {
|
||||
id = _constructPrimitiveIndex.pop();
|
||||
Primitive* primitive = _primitives[id];
|
||||
if (primitive) {
|
||||
constructElements(primitive);
|
||||
|
||||
// No need to keep an extra copy of the vertices
|
||||
_cpuMemoryUsage -= primitive->getMemoryUsage();
|
||||
primitive->releaseVertexElements();
|
||||
_cpuMemoryUsage += primitive->getMemoryUsage();
|
||||
}
|
||||
}
|
||||
|
||||
// The application uses clockwise winding for the definition of front face, this renderer
|
||||
// aalso uses clockwise (that is the gl default) to construct the triangulation
|
||||
// so...
|
||||
//glFrontFace(GL_CW);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(VertexElement), 0);
|
||||
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glNormalPointer(GL_FLOAT, sizeof(VertexElement), (const GLvoid*)12);
|
||||
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexElement), (const GLvoid*)24);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId);
|
||||
glDrawElements(GL_TRIANGLES, 3 * _triElementCount, GL_UNSIGNED_INT, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
unsigned long PrimitiveRenderer::vGetMemoryUsage() {
|
||||
return _cpuMemoryUsage;
|
||||
}
|
||||
|
||||
unsigned long PrimitiveRenderer::vGetMemoryUsageGPU() {
|
||||
return _gpuMemoryUsage;
|
||||
}
|
|
@ -1,503 +0,0 @@
|
|||
//
|
||||
// PrimitiveRenderer.h
|
||||
// interface/src/voxels
|
||||
//
|
||||
// Created by Norman Craft.
|
||||
// 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_PrimitiveRenderer_h
|
||||
#define hifi_PrimitiveRenderer_h
|
||||
|
||||
#include <QStack>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
|
||||
/// Vertex element structure.
|
||||
/// Using the array of structures approach to specifying
|
||||
/// vertex data to GL cuts down on the calls to glBufferSubData
|
||||
///
|
||||
typedef
|
||||
struct __VertexElement {
|
||||
struct __position {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} position;
|
||||
struct __normal {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} normal;
|
||||
struct __color {
|
||||
unsigned char r;
|
||||
unsigned char g;
|
||||
unsigned char b;
|
||||
unsigned char a;
|
||||
} color;
|
||||
} VertexElement;
|
||||
|
||||
/// Triangle element index structure.
|
||||
/// Specify the vertex indices of the triangle and its element index.
|
||||
///
|
||||
typedef
|
||||
struct __TriElement {
|
||||
int indices[3];
|
||||
int id;
|
||||
|
||||
} TriElement;
|
||||
|
||||
/// Vertex element list container.
|
||||
///
|
||||
typedef QVector<VertexElement *> VertexElementList;
|
||||
|
||||
/// Vertex element index list container.
|
||||
///
|
||||
typedef QVector<int> VertexElementIndexList;
|
||||
|
||||
/// Triangle element list container
|
||||
///
|
||||
typedef QVector<TriElement *> TriElementList;
|
||||
|
||||
///
|
||||
/// @class Primitive
|
||||
/// Primitive Interface class.
|
||||
/// Abstract class for accessing vertex and tri elements of geometric primitives
|
||||
///
|
||||
///
|
||||
class Primitive {
|
||||
public:
|
||||
virtual ~Primitive();
|
||||
|
||||
// API methods go here
|
||||
|
||||
/// Vertex element accessor.
|
||||
/// @return A list of vertex elements of the primitive
|
||||
///
|
||||
const VertexElementList& vertexElements() const;
|
||||
|
||||
/// Vertex element index accessor.
|
||||
/// @return A list of vertex element indices of the primitive
|
||||
///
|
||||
VertexElementIndexList& vertexElementIndices();
|
||||
|
||||
/// Tri element accessor.
|
||||
/// @return A list of tri elements of the primitive
|
||||
///
|
||||
TriElementList& triElements();
|
||||
|
||||
/// Release vertex elements.
|
||||
///
|
||||
void releaseVertexElements();
|
||||
|
||||
/// Get memory usage.
|
||||
///
|
||||
unsigned long getMemoryUsage();
|
||||
|
||||
protected:
|
||||
/// Default constructor prohibited to API user, restricted to service implementer.
|
||||
///
|
||||
Primitive();
|
||||
|
||||
private:
|
||||
/// Copy constructor prohibited.
|
||||
///
|
||||
Primitive(
|
||||
const Primitive& copy
|
||||
);
|
||||
|
||||
// SPI methods are defined here
|
||||
|
||||
/// Vertex element accessor.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual const VertexElementList& vVertexElements() const = 0;
|
||||
|
||||
/// Vertex element index accessor.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual VertexElementIndexList& vVertexElementIndices() = 0;
|
||||
|
||||
/// Tri element accessor.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual TriElementList& vTriElements() = 0;
|
||||
|
||||
/// Release vertex elements.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual void vReleaseVertexElements() = 0;
|
||||
|
||||
/// Get memory usage.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual unsigned long vGetMemoryUsage() = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// @class Cube
|
||||
/// Class for accessing the vertex and triangle elements of a cube
|
||||
///
|
||||
class Cube: public Primitive {
|
||||
public:
|
||||
/// Configuration dependency injection constructor.
|
||||
///
|
||||
Cube(
|
||||
float x, ///< Cube location on X-axis
|
||||
float y, ///< Cube location on Y-axis
|
||||
float z, ///< Cube location on Z-axis
|
||||
float s, ///< Cube size
|
||||
unsigned char r, ///< Cube red color component
|
||||
unsigned char g, ///< Cube green color component
|
||||
unsigned char b, ///< Cube blue color component
|
||||
unsigned char faces ///< Bitmask of faces of cube excluded from construction
|
||||
);
|
||||
|
||||
~Cube();
|
||||
|
||||
private:
|
||||
/// Copy constructor prohibited.
|
||||
///
|
||||
Cube (
|
||||
const Cube& cube
|
||||
);
|
||||
|
||||
/// Cube initialization
|
||||
///
|
||||
void init(
|
||||
float x, ///< Cube location on X-axis
|
||||
float y, ///< Cube location on Y-axis
|
||||
float z, ///< Cube location on Z-axis
|
||||
float s, ///< Cube size
|
||||
unsigned char r, ///< Cube red color component
|
||||
unsigned char g, ///< Cube green color component
|
||||
unsigned char b, ///< Cube blue color component
|
||||
unsigned char faceExclusions ///< Bitmask of faces of cube excluded from construction
|
||||
);
|
||||
|
||||
/// Cube termination
|
||||
///
|
||||
void terminate();
|
||||
|
||||
/// Initialize cube's vertex list
|
||||
///
|
||||
void initializeVertices(
|
||||
float x, ///< Cube location on X-axis
|
||||
float y, ///< Cube location on Y-axis
|
||||
float z, ///< Cube location on Z-axis
|
||||
float s, ///< Cube size
|
||||
unsigned char r, ///< Cube red color component
|
||||
unsigned char g, ///< Cube green color component
|
||||
unsigned char b, ///< Cube blue color component
|
||||
unsigned char faceExclusions ///< Bitmask of faces of cube excluded from construction
|
||||
);
|
||||
|
||||
/// Terminate cube's vertex list
|
||||
///
|
||||
void terminateVertices();
|
||||
|
||||
/// Initialize cube's triangle list
|
||||
///
|
||||
void initializeTris(
|
||||
unsigned char faceExclusions
|
||||
);
|
||||
|
||||
/// Terminate cube's triangle list
|
||||
///
|
||||
void terminateTris();
|
||||
|
||||
// SPI virtual override methods go here
|
||||
|
||||
const VertexElementList& vVertexElements() const;
|
||||
VertexElementIndexList& vVertexElementIndices();
|
||||
TriElementList& vTriElements();
|
||||
void vReleaseVertexElements();
|
||||
unsigned long vGetMemoryUsage();
|
||||
|
||||
private:
|
||||
VertexElementList _vertices; ///< Vertex element list
|
||||
VertexElementIndexList _vertexIndices; ///< Vertex element index list
|
||||
TriElementList _tris; ///< Tri element list
|
||||
|
||||
unsigned long _cpuMemoryUsage; ///< Memory allocation of object
|
||||
|
||||
static const int _sNumFacesPerCube = 6; ///< Number of faces per cube
|
||||
static const int _sNumVerticesPerCube = 24; ///< Number of vertices per cube
|
||||
static unsigned char _sFaceIndexToHalfSpaceMask[6]; ///< index to bitmask map
|
||||
static float _sVertexIndexToConstructionVector[24][3]; ///< Vertex index to construction vector map
|
||||
static float _sVertexIndexToNormalVector[6][3]; ///< Vertex index to normal vector map
|
||||
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// @class Renderer
|
||||
/// GL renderer interface class.
|
||||
/// Abstract class for rendering geometric primitives in GL
|
||||
///
|
||||
class Renderer {
|
||||
public:
|
||||
virtual ~Renderer();
|
||||
|
||||
// API methods go here
|
||||
|
||||
/// Add primitive to renderer database.
|
||||
///
|
||||
int add(
|
||||
Primitive* primitive ///< Primitive instance to be added
|
||||
);
|
||||
|
||||
/// Remove primitive from renderer database.
|
||||
///
|
||||
void remove(
|
||||
int id ///< Primitive id to be removed
|
||||
);
|
||||
|
||||
/// Clear all primitives from renderer database
|
||||
///
|
||||
void release();
|
||||
|
||||
/// Render primitive database.
|
||||
/// The render method assumes appropriate GL context and state has
|
||||
/// already been provided for
|
||||
///
|
||||
void render();
|
||||
|
||||
/// Get memory usage.
|
||||
///
|
||||
unsigned long getMemoryUsage();
|
||||
|
||||
/// Get GPU memory usage.
|
||||
///
|
||||
unsigned long getMemoryUsageGPU();
|
||||
|
||||
protected:
|
||||
/// Default constructor prohibited to API user, restricted to service implementer.
|
||||
///
|
||||
Renderer();
|
||||
|
||||
private:
|
||||
/// Copy constructor prohibited.
|
||||
///
|
||||
Renderer(
|
||||
const Renderer& copy
|
||||
);
|
||||
|
||||
// SPI methods are defined here
|
||||
|
||||
/// Add primitive to renderer database.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
/// @return Primitive id
|
||||
///
|
||||
virtual int vAdd(
|
||||
Primitive* primitive ///< Primitive instance to be added
|
||||
) = 0;
|
||||
|
||||
/// Remove primitive from renderer database.
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual void vRemove(
|
||||
int id ///< Primitive id
|
||||
) = 0;
|
||||
|
||||
/// Clear all primitives from renderer database
|
||||
/// Service implementer to provide private override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual void vRelease() = 0;
|
||||
|
||||
/// Render primitive database.
|
||||
/// Service implementer to provide private virtual override for this method
|
||||
/// in derived class
|
||||
///
|
||||
virtual void vRender() = 0;
|
||||
|
||||
/// Get memory usage.
|
||||
///
|
||||
virtual unsigned long vGetMemoryUsage() = 0;
|
||||
|
||||
/// Get GPU memory usage.
|
||||
///
|
||||
virtual unsigned long vGetMemoryUsageGPU() = 0;
|
||||
|
||||
};
|
||||
|
||||
///
|
||||
/// @class PrimitiveRenderer
|
||||
/// Renderer implementation class for the rendering of geometric primitives
|
||||
/// using GL element array and GL array buffers
|
||||
///
|
||||
class PrimitiveRenderer : public Renderer {
|
||||
public:
|
||||
/// Configuration dependency injection constructor.
|
||||
///
|
||||
PrimitiveRenderer(
|
||||
int maxCount ///< Max count
|
||||
);
|
||||
|
||||
~PrimitiveRenderer();
|
||||
|
||||
private:
|
||||
/// Default constructor prohibited.
|
||||
///
|
||||
PrimitiveRenderer();
|
||||
|
||||
/// Copy constructor prohibited.
|
||||
///
|
||||
PrimitiveRenderer(
|
||||
const PrimitiveRenderer& renderer
|
||||
);
|
||||
|
||||
void init();
|
||||
void terminate();
|
||||
|
||||
/// Allocate and initialize GL buffers.
|
||||
///
|
||||
void initializeGL();
|
||||
|
||||
/// Terminate and deallocate GL buffers.
|
||||
///
|
||||
void terminateGL();
|
||||
|
||||
void initializeBookkeeping();
|
||||
void terminateBookkeeping();
|
||||
|
||||
/// Construct the elements of the faces of the primitive.
|
||||
///
|
||||
void constructElements(
|
||||
Primitive* primitive ///< Primitive instance
|
||||
);
|
||||
|
||||
/// Deconstruct the elements of the faces of the primitive.
|
||||
///
|
||||
void deconstructElements(
|
||||
Primitive* primitive ///< Primitive instance
|
||||
);
|
||||
|
||||
/// Deconstruct the triangle element from the GL buffer.
|
||||
///
|
||||
void deconstructTriElement(
|
||||
int idx ///< Triangle element index
|
||||
);
|
||||
|
||||
/// Deconstruct the vertex element from the GL buffer.
|
||||
///
|
||||
void deconstructVertexElement(
|
||||
int idx ///< Vertex element index
|
||||
);
|
||||
|
||||
/// Transfer the vertex element to the GL buffer.
|
||||
///
|
||||
void transferVertexElement(
|
||||
int idx, ///< Vertex element index
|
||||
VertexElement *vertex ///< Vertex element instance
|
||||
);
|
||||
|
||||
/// Transfer the triangle element to the GL buffer.
|
||||
///
|
||||
void transferTriElement(
|
||||
int idx, ///< Triangle element index
|
||||
int tri[3] ///< Triangle element data
|
||||
);
|
||||
|
||||
/// Get available primitive index.
|
||||
/// Get an available primitive index from either the recycling
|
||||
/// queue or incrementing the counter
|
||||
///
|
||||
int getAvailablePrimitiveIndex();
|
||||
|
||||
/// Get available vertex element index.
|
||||
/// Get an available vertex element index from either the recycling
|
||||
/// queue or incrementing the counter
|
||||
///
|
||||
int getAvailableVertexElementIndex();
|
||||
|
||||
/// Get available triangle element index.
|
||||
/// Get an available triangle element index from either the elements
|
||||
/// scheduled for deconstruction queue, the recycling
|
||||
/// queue or incrementing the counter
|
||||
///
|
||||
int getAvailableTriElementIndex();
|
||||
|
||||
// SPI virtual override methods go here
|
||||
|
||||
/// Add primitive to renderer database.
|
||||
///
|
||||
int vAdd(
|
||||
Primitive* primitive ///< Primitive instance to be added
|
||||
);
|
||||
|
||||
/// Remove primitive from renderer database.
|
||||
///
|
||||
void vRemove(
|
||||
int id ///< Primitive id to be removed
|
||||
);
|
||||
|
||||
/// Clear all primitives from renderer database
|
||||
///
|
||||
void vRelease();
|
||||
|
||||
/// Render triangle database.
|
||||
///
|
||||
void vRender();
|
||||
|
||||
/// Get memory usage.
|
||||
///
|
||||
unsigned long vGetMemoryUsage();
|
||||
|
||||
/// Get gpu memory usage.
|
||||
///
|
||||
unsigned long vGetMemoryUsageGPU();
|
||||
|
||||
private:
|
||||
|
||||
int _maxCount; ///< Maximum count of tris
|
||||
|
||||
// GL related parameters
|
||||
|
||||
GLuint _triBufferId; ///< GL element array buffer id
|
||||
GLuint _vertexBufferId; ///< GL vertex array buffer id
|
||||
|
||||
// Book keeping parameters
|
||||
|
||||
int _vertexElementCount; ///< Count of vertices
|
||||
int _maxVertexElementCount; ///< Max count of vertices
|
||||
|
||||
int _triElementCount; ///< Count of triangles
|
||||
int _maxTriElementCount; ///< Max count of triangles
|
||||
|
||||
QVector<Primitive *> _primitives; ///< Vector of primitive
|
||||
int _primitiveCount; ///< Count of primitives
|
||||
|
||||
QStack<int> _availablePrimitiveIndex; ///< Queue of primitive indices available
|
||||
QStack<int> _availableVertexElementIndex; ///< Queue of vertex element indices available
|
||||
QStack<int> _availableTriElementIndex; ///< Queue of triangle element indices available
|
||||
QStack<int> _deconstructTriElementIndex; ///< Queue of triangle element indices requiring deletion from GL
|
||||
QStack<int> _constructPrimitiveIndex; ///< Queue of primitives requiring addition to GL
|
||||
|
||||
QMutex _guard;
|
||||
|
||||
// Statistics parameters, not necessary for proper operation
|
||||
|
||||
unsigned long _gpuMemoryUsage; ///< GPU memory used by this instance
|
||||
unsigned long _cpuMemoryUsage; ///< CPU memory used by this instance
|
||||
|
||||
|
||||
static const int _sIndicesPerTri = 3;
|
||||
static const int _sBytesPerTriElement = sizeof(GLint) * _sIndicesPerTri;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_PrimitiveRenderer_h
|
File diff suppressed because it is too large
Load diff
|
@ -25,22 +25,12 @@
|
|||
#include "Camera.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "renderer/VoxelShader.h"
|
||||
#include "PrimitiveRenderer.h"
|
||||
|
||||
class ProgramObject;
|
||||
|
||||
const int NUM_CHILDREN = 8;
|
||||
|
||||
|
||||
struct VoxelShaderVBOData
|
||||
{
|
||||
float x, y, z; // position
|
||||
float s; // size
|
||||
unsigned char r,g,b; // color
|
||||
};
|
||||
|
||||
|
||||
class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -80,7 +70,6 @@ public:
|
|||
void killLocalVoxels();
|
||||
|
||||
virtual void hideOutOfView(bool forceFullFrustum = false);
|
||||
void inspectForOcclusions();
|
||||
bool hasViewChanged();
|
||||
bool isViewChanging();
|
||||
|
||||
|
@ -97,8 +86,6 @@ public slots:
|
|||
void clearAllNodesBufferIndex();
|
||||
|
||||
void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline);
|
||||
void setUseVoxelShader(bool useVoxelShader);
|
||||
void setVoxelsAsPoints(bool voxelsAsPoints);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
|
@ -140,8 +127,6 @@ private:
|
|||
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
|
||||
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
|
||||
static bool inspectForExteriorOcclusionsOperation(OctreeElement* element, void* extraData);
|
||||
static bool inspectForInteriorOcclusionsOperation(OctreeElement* element, void* extraData);
|
||||
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
|
||||
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -191,14 +176,8 @@ private:
|
|||
void initVoxelMemory();
|
||||
void cleanupVoxelMemory();
|
||||
|
||||
bool _useVoxelShader;
|
||||
bool _voxelsAsPoints;
|
||||
bool _voxelShaderModeWhenVoxelsAsPointsEnabled;
|
||||
|
||||
GLuint _vboVoxelsID; /// when using voxel shader, we'll use this VBO
|
||||
GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes
|
||||
VoxelShaderVBOData* _writeVoxelShaderData;
|
||||
VoxelShaderVBOData* _readVoxelShaderData;
|
||||
|
||||
GLuint _vboVerticesID;
|
||||
GLuint _vboColorsID;
|
||||
|
@ -258,17 +237,6 @@ private:
|
|||
float _lastKnownVoxelSizeScale;
|
||||
int _lastKnownBoundaryLevelAdjust;
|
||||
|
||||
bool _inOcclusions;
|
||||
bool _showCulledSharedFaces; ///< Flag visibility of culled faces
|
||||
bool _usePrimitiveRenderer; ///< Flag primitive renderer for use
|
||||
PrimitiveRenderer* _renderer; ///< Voxel renderer
|
||||
|
||||
static const unsigned int _sNumOctantsPerHemiVoxel = 4;
|
||||
static int _sCorrectedChildIndex[8];
|
||||
static unsigned short _sSwizzledOcclusionBits[64]; ///< Swizzle value of bit pairs of the value of index
|
||||
static unsigned char _sOctantIndexToBitMask[8]; ///< Map octant index to partition mask
|
||||
static unsigned char _sOctantIndexToSharedBitMask[8][8]; ///< Map octant indices to shared partition mask
|
||||
|
||||
// haze
|
||||
bool _drawHaze;
|
||||
float _farHazeDistance;
|
||||
|
|
|
@ -138,7 +138,15 @@ static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
|
|||
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
||||
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();
|
||||
|
||||
template<class T> QVariant readBinaryArray(QDataStream& in) {
|
||||
template<class T> int streamSize() {
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
template<bool> int streamSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||
quint32 arrayLength;
|
||||
quint32 encoding;
|
||||
quint32 compressedLength;
|
||||
|
@ -146,6 +154,7 @@ template<class T> QVariant readBinaryArray(QDataStream& in) {
|
|||
in >> arrayLength;
|
||||
in >> encoding;
|
||||
in >> compressedLength;
|
||||
position += sizeof(quint32) * 3;
|
||||
|
||||
QVector<T> values;
|
||||
const unsigned int DEFLATE_ENCODING = 1;
|
||||
|
@ -154,6 +163,7 @@ template<class T> QVariant readBinaryArray(QDataStream& in) {
|
|||
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||
position += compressedLength;
|
||||
QByteArray uncompressed = qUncompress(compressed);
|
||||
QDataStream uncompressedIn(uncompressed);
|
||||
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
||||
|
@ -167,65 +177,74 @@ template<class T> QVariant readBinaryArray(QDataStream& in) {
|
|||
for (quint32 i = 0; i < arrayLength; i++) {
|
||||
T value;
|
||||
in >> value;
|
||||
position += streamSize<T>();
|
||||
values.append(value);
|
||||
}
|
||||
}
|
||||
return QVariant::fromValue(values);
|
||||
}
|
||||
|
||||
QVariant parseBinaryFBXProperty(QDataStream& in) {
|
||||
QVariant parseBinaryFBXProperty(QDataStream& in, int& position) {
|
||||
char ch;
|
||||
in.device()->getChar(&ch);
|
||||
position++;
|
||||
switch (ch) {
|
||||
case 'Y': {
|
||||
qint16 value;
|
||||
in >> value;
|
||||
position += sizeof(qint16);
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'C': {
|
||||
bool value;
|
||||
in >> value;
|
||||
position++;
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'I': {
|
||||
qint32 value;
|
||||
in >> value;
|
||||
position += sizeof(qint32);
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'F': {
|
||||
float value;
|
||||
in >> value;
|
||||
position += sizeof(float);
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'D': {
|
||||
double value;
|
||||
in >> value;
|
||||
position += sizeof(double);
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'L': {
|
||||
qint64 value;
|
||||
in >> value;
|
||||
position += sizeof(qint64);
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
case 'f': {
|
||||
return readBinaryArray<float>(in);
|
||||
return readBinaryArray<float>(in, position);
|
||||
}
|
||||
case 'd': {
|
||||
return readBinaryArray<double>(in);
|
||||
return readBinaryArray<double>(in, position);
|
||||
}
|
||||
case 'l': {
|
||||
return readBinaryArray<qint64>(in);
|
||||
return readBinaryArray<qint64>(in, position);
|
||||
}
|
||||
case 'i': {
|
||||
return readBinaryArray<qint32>(in);
|
||||
return readBinaryArray<qint32>(in, position);
|
||||
}
|
||||
case 'b': {
|
||||
return readBinaryArray<bool>(in);
|
||||
return readBinaryArray<bool>(in, position);
|
||||
}
|
||||
case 'S':
|
||||
case 'R': {
|
||||
quint32 length;
|
||||
in >> length;
|
||||
position += sizeof(quint32) + length;
|
||||
return QVariant::fromValue(in.device()->read(length));
|
||||
}
|
||||
default:
|
||||
|
@ -233,8 +252,8 @@ QVariant parseBinaryFBXProperty(QDataStream& in) {
|
|||
}
|
||||
}
|
||||
|
||||
FBXNode parseBinaryFBXNode(QDataStream& in) {
|
||||
quint32 endOffset;
|
||||
FBXNode parseBinaryFBXNode(QDataStream& in, int& position) {
|
||||
qint32 endOffset;
|
||||
quint32 propertyCount;
|
||||
quint32 propertyListLength;
|
||||
quint8 nameLength;
|
||||
|
@ -243,21 +262,23 @@ FBXNode parseBinaryFBXNode(QDataStream& in) {
|
|||
in >> propertyCount;
|
||||
in >> propertyListLength;
|
||||
in >> nameLength;
|
||||
position += sizeof(quint32) * 3 + sizeof(quint8);
|
||||
|
||||
FBXNode node;
|
||||
const unsigned int MIN_VALID_OFFSET = 40;
|
||||
const int MIN_VALID_OFFSET = 40;
|
||||
if (endOffset < MIN_VALID_OFFSET || nameLength == 0) {
|
||||
// use a null name to indicate a null node
|
||||
return node;
|
||||
}
|
||||
node.name = in.device()->read(nameLength);
|
||||
position += nameLength;
|
||||
|
||||
for (quint32 i = 0; i < propertyCount; i++) {
|
||||
node.properties.append(parseBinaryFBXProperty(in));
|
||||
node.properties.append(parseBinaryFBXProperty(in, position));
|
||||
}
|
||||
|
||||
while (endOffset > in.device()->pos()) {
|
||||
FBXNode child = parseBinaryFBXNode(in);
|
||||
while (endOffset > position) {
|
||||
FBXNode child = parseBinaryFBXNode(in, position);
|
||||
if (child.name.isNull()) {
|
||||
return node;
|
||||
|
||||
|
@ -416,11 +437,12 @@ FBXNode parseFBX(QIODevice* device) {
|
|||
// skip the rest of the header
|
||||
const int HEADER_SIZE = 27;
|
||||
in.skipRawData(HEADER_SIZE);
|
||||
int position = HEADER_SIZE;
|
||||
|
||||
// parse the top-level node
|
||||
FBXNode top;
|
||||
while (device->bytesAvailable()) {
|
||||
FBXNode next = parseBinaryFBXNode(in);
|
||||
FBXNode next = parseBinaryFBXNode(in, position);
|
||||
if (next.name.isNull()) {
|
||||
return top;
|
||||
|
||||
|
@ -2516,7 +2538,11 @@ QByteArray writeMapping(const QVariantHash& mapping) {
|
|||
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps, lightmapLevel);
|
||||
return readFBX(&buffer, mapping, loadLightmaps, lightmapLevel);
|
||||
}
|
||||
|
||||
FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) {
|
||||
return extractFBXGeometry(parseFBX(device), mapping, loadLightmaps, lightmapLevel);
|
||||
}
|
||||
|
||||
bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class FBXNode;
|
||||
|
||||
typedef QList<FBXNode> FBXNodeList;
|
||||
|
@ -272,6 +274,10 @@ QByteArray writeMapping(const QVariantHash& mapping);
|
|||
/// \exception QString if an error occurs in parsing
|
||||
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||
|
||||
/// Reads SVO geometry from the supplied model data.
|
||||
FBXGeometry readSVO(const QByteArray& model);
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ void DomainHandler::hardReset() {
|
|||
|
||||
qDebug() << "Hard reset in NodeList DomainHandler.";
|
||||
_iceDomainID = QUuid();
|
||||
_iceServerSockAddr = HifiSockAddr();
|
||||
_hostname = QString();
|
||||
_sockAddr.setAddress(QHostAddress::Null);
|
||||
}
|
||||
|
|
|
@ -728,6 +728,10 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
}
|
||||
|
||||
recurseTreeWithOperation(findRayIntersectionOp, &args);
|
||||
|
||||
if (args.found) {
|
||||
args.distance *= (float)(TREE_SCALE); // scale back up to meters
|
||||
}
|
||||
|
||||
if (gotLock) {
|
||||
unlock();
|
||||
|
|
|
@ -39,6 +39,7 @@ void registerMetaTypes(QScriptEngine* engine) {
|
|||
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) {
|
||||
|
@ -206,3 +207,14 @@ void quuidFromScriptValue(const QScriptValue& object, QUuid& uuid) {
|
|||
uuid = fromString;
|
||||
}
|
||||
|
||||
QScriptValue qSizeFToScriptValue(QScriptEngine* engine, const QSizeF& qSizeF) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("width", qSizeF.width());
|
||||
obj.setProperty("height", qSizeF.height());
|
||||
return obj;
|
||||
}
|
||||
|
||||
void qSizeFFromScriptValue(const QScriptValue& object, QSizeF& qSizeF) {
|
||||
qSizeF.setWidth(object.property("width").toVariant().toFloat());
|
||||
qSizeF.setHeight(object.property("height").toVariant().toFloat());
|
||||
}
|
||||
|
|
|
@ -81,4 +81,8 @@ void collisionFromScriptValue(const QScriptValue &object, Collision& collision);
|
|||
QScriptValue quuidToScriptValue(QScriptEngine* engine, const QUuid& uuid);
|
||||
void quuidFromScriptValue(const QScriptValue& object, QUuid& uuid);
|
||||
|
||||
//Q_DECLARE_METATYPE(QSizeF) // Don't need to to this becase it's arleady a meta type
|
||||
QScriptValue qSizeFToScriptValue(QScriptEngine* engine, const QSizeF& qSizeF);
|
||||
void qSizeFFromScriptValue(const QScriptValue& object, QSizeF& qSizeF);
|
||||
|
||||
#endif // hifi_RegisteredMetaTypes_h
|
||||
|
|
|
@ -9,21 +9,43 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "ShutdownEventListener.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
||||
ShutdownEventListener::ShutdownEventListener(QObject* parent) : QObject(parent) {
|
||||
ShutdownEventListener& ShutdownEventListener::getInstance() {
|
||||
static ShutdownEventListener staticInstance;
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
void signalHandler(int param) {
|
||||
// tell the qApp it should quit
|
||||
QMetaObject::invokeMethod(qApp, "quit");
|
||||
}
|
||||
|
||||
ShutdownEventListener::ShutdownEventListener(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
// be a signal handler for SIGTERM so we can stop our children when we get it
|
||||
signal(SIGTERM, signalHandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) {
|
||||
#ifdef Q_OS_WIN
|
||||
if (eventType == "windows_generic_MSG") {
|
||||
MSG* message = (MSG*)msg;
|
||||
if (message->message == WM_CLOSE) {
|
||||
emit receivedCloseEvent();
|
||||
// tell our registered application to quit
|
||||
QMetaObject::invokeMethod(qApp, "quit");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -18,12 +18,11 @@
|
|||
class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShutdownEventListener(QObject* parent = NULL);
|
||||
static ShutdownEventListener& getInstance();
|
||||
|
||||
virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result);
|
||||
|
||||
signals:
|
||||
void receivedCloseEvent();
|
||||
private:
|
||||
ShutdownEventListener(QObject* parent = 0);
|
||||
};
|
||||
|
||||
#endif // hifi_ShutdownEventListener_h
|
||||
|
|
Loading…
Reference in a new issue